MIPSfpga and UART
A little over a month has passed since I ported the open source UART16550 module to the AHB-Lite bus. Writing about it at that time was somewhat not logical, since an article about interrupting MIPSfpga had not yet been published.
If you are an experienced developer, then there is only one useful news for you: UART16550 has been added to the MIPSfpga-plus system, you can not read further. And for those who are interested in the disassembled example of the use of this module - welcome to cat.
Introduction
The reader is supposed to:
- familiar with the subject area in the volume of the textbook Harris and Harris [ L1 ];
- has access to the source codes MIPSfpga [ L2 ] and mipsfpga-plus [ L3 ];
- He has some experience in programming microcontrollers (of any architecture), including the use of UART. You can refresh what UART is like here [ L4 ];
What is UART16550?
The history of the appearance of this microcircuit is well described in [ L5 ], you can find documentation for it in google [ L6 ], something else is important for us:
- it is extremely widespread;
- work with it is supported by the Linux kernel;
- an open source implementation for verilog [ L7 , L8 , L9 ] has existed for it for a long time , but for the Wishbone bus;
- The cost of porting this implementation to the AHB-Lite bus used in MIPSfpga is about 100 lines of code [ S1 ]:
module mfp_ahb_lite_uart16550(
//ABB-Lite side
input HCLK,
input HRESETn,
input [ 31 : 0 ] HADDR,
input [ 2 : 0 ] HBURST,
input HMASTLOCK, // ignored
input [ 3 : 0 ] HPROT, // ignored
input HSEL,
input [ 2 : 0 ] HSIZE,
input [ 1 : 0 ] HTRANS,
input [ 31 : 0 ] HWDATA,
input HWRITE,
output reg [ 31 : 0 ] HRDATA,
output HREADY,
output HRESP,
input SI_Endian, // ignored//UART side
input UART_SRX, // UART serial input signal
output UART_STX, // UART serial output signal
output UART_RTS, // UART MODEM Request To Send
input UART_CTS, // UART MODEM Clear To Send
output UART_DTR, // UART MODEM Data Terminal Ready
input UART_DSR, // UART MODEM Data Set Ready
input UART_RI, // UART MODEM Ring Indicator
input UART_DCD, // UART MODEM Data Carrier Detect//UART internal
output UART_BAUD, // UART baudrate output
output UART_INT// UART interrupt
);
parameter S_INIT = 0,
S_IDLE = 1,
S_READ = 2,
S_WRITE = 3;
reg [ 1:0 ] State, Next;
assign HRESP = 1'b0;
assign HREADY = (State == S_IDLE);
always @ (posedge HCLK) begin
if (~HRESETn)
State <= S_INIT;
elseState <= Next;
end
reg [ 2:0 ] ADDR_old;
wire [ 2:0 ] ADDR = HADDR [ 4:2 ];
wire [ 7:0 ] ReadData;
parameter HTRANS_IDLE = 2'b0;
wire NeedAction = HTRANS != HTRANS_IDLE && HSEL;
always @ (*) begin
//State change decisioncase(State)
default : Next = S_IDLE;
S_IDLE : Next = ~NeedAction ? S_IDLE : (
HWRITE ? S_WRITE : S_READ );
endcase
end
always @ (posedge HCLK) begin
case(State)
S_INIT : ;
S_IDLE : if(HSEL) ADDR_old <= ADDR;
S_READ : HRDATA <= { 24'b0, ReadData};
S_WRITE : ;
endcase
end
wire [ 7:0 ] WriteData = HWDATA [ 7:0 ];
wire [ 2:0 ] ActionAddr;
wire WriteAction;
wire ReadAction;
reg [ 10:0 ] conf;
assign { ReadAction, WriteAction, ActionAddr } = conf;
always @ (*) begin
//iocase(State)
default : conf = { 2'b00, 8'b0 };
S_READ : conf = { 2'b10, ADDR };
S_WRITE : conf = { 2'b01, ADDR_old };
endcase
end
// Registers
uart_regs regs(
.clk ( HCLK ),
.wb_rst_i ( ~HRESETn ),
.wb_addr_i ( ActionAddr ),
.wb_dat_i ( WriteData ),
.wb_dat_o ( ReadData ),
.wb_we_i ( WriteAction ),
.wb_re_i ( ReadAction ),
.modem_inputs ( { UART_CTS, UART_DSR, UART_RI, UART_DCD } ),
.stx_pad_o ( UART_STX ),
.srx_pad_i ( UART_SRX ),
.rts_pad_o ( UART_RTS ),
.dtr_pad_o ( UART_DTR ),
.int_o ( UART_INT ),
.baud_o ( UART_BAUD )
);
endmodule
Key Implementation Features
- the project [ L8 ] was taken as a basic solution , which, in turn, is based on [ L7 , L9 ];
- Compared to the basic solution, the code associated with the Wishbone bus has been excluded from the project. It has been replaced with the corresponding interface module for AHB-Lite (mfp_ahb_lite_uart16550) [ S1 ];
- all code borrowed from the base project is located in the uart16550 [ S2 ] directory ;
- the mfp_ahb_lite_uart16550 module is included in the system on an ongoing basis, the MFP_USE_DUPLEX_UART [ S3 ] option in the mfp_ahb_lite_matrix_config.vh file determines the availability of UART_SRX and UART_STX signals ;
- for existing UART_RX and UART_TX signals , the following usage procedure is provided:
UART_RX - regardless of the mode, it is used only to download firmware to the system’s memory, as it was before (the mfp_uart_receiver module does not apply to UART16550). If the MFP_USE_DUPLEX_UART option is not active, then UART_TX is used to transfer data from the UART16550 to the outside; reception is not available in this case. If the MFP_USE_DUPLEX_UART option is active, then UART_TX are not used; UART_SRX and UART_STX [ S4 ] are used to transfer data from / to mfp_ahb_lite_uart16550 ; - the output of the modem control interface to the top level of the module was not performed. Corresponding lines are available in the interface of the ahb_lite_uart16550 module and can be used if necessary ( UART_RTS, UART_CTS, UART_DTR, UART_DSR, UART_RI, UART_DCD ) [ S5 ];
- interrupt signal ( UART_INT ) is connected to input hw3 (signal SI_Int [3] ) for backward compatibility and vector modes. And to eic5 (signal EIC_input [5] ) for the external interrupt controller [ S6 ];
- when running in simulation mode, the UART_STX line is closed to UART_SRX [ S7 ];
- the efficiency of the obtained solution in simulation mode was checked using Modelsim;
- operability on hardware was tested on a Terasic DE10-Lite board [ L10 ];
- As an example of working with the UART16550, two programs were written: 05_uart [ S8 ] and 08_uart_irq [ S9 ]: after the reset, the uart (8n1, 115200) settings are sent, a greeting is sent, after which the code of each received character is displayed on the LED and 7-segment indicators. When starting on the board, each character received by uart is sent back;
- debugging of the developed module is available "in isolation" from the mipsfpga + code within the framework of the project [ L11 ];
- for software configuration of the module, use the documentation of the base project, [ D1 ]. I recommend that you familiarize yourself with it in order to better understand the example below;
- work with the AHB-Lite bus is described in detail in [ D2 ];
Example
Launch order
check that the following setting [ S3 ] is set in the mfp_ahb_lite_matrix_config.vh file :
`define MFP_USE_DUPLEX_UART
- go to the directory with the program: mipsfpga-plus / programs / 05_uart [ S8 ], or 08_uart_irq [ S9 ];
in the main.c file set [ S10 ]:
#define RUNTYPE SIMULATION
build the program and run it in the simulator:
02_compile_and_link.bat 05_generate_verilog_readmemh_file.bat 06_simulate_with_modelsim.bat
- after completion of the simulation script, click “no” to prevent the simulator from closing;
- the way this code works "on hardware" is visible on the header gif.
Description of the program and system configuration
- since the example 08_uart_irq provides, in addition to configuring the UART16550 itself, also using the interrupts it generates, then this configuration is considered below;
- directives providing work with controller registers are given in the header file uart16550.h [ S11 ];
at startup, the UART16550 [ S12 ] is configured :
void uartInit(uint16_t divisor) { // 8n1 uart mode MFP_UART_LCR = MFP_UART_LCR_8N1; // Divisor Latches access enable MFP_UART_LCR |= MFP_UART_LCR_LATCH;// Divisor LSB MFP_UART_DLL = divisor & 0xFF;// Divisor MSB MFP_UART_DLH = (divisor >> 8) & 0xff;// Divisor Latches access disable MFP_UART_LCR &= ~MFP_UART_LCR_LATCH;//enable Received Data available interrupt MFP_UART_IER = MFP_UART_IER_RDA; //set 4 byte Receiver FIFO Interrupt trigger level MFP_UART_FCR = MFP_UART_FCR_ITL4; }
interrupt setting [ S13 ] (detailed in [ L12 ]):
void mipsInterruptInit(void) { // Status.BEV 0 - vector interrupt mode mips32_bicsr (SR_BEV); // Cause.IV, 1 - special int vector (0x200) // where0x200 - base for others interrupts; mips32_biscr (CR_IV); // get IntCtl reg value uint32_t intCtl = mips32_getintctl(); // set interrupt table vector spacing (0x20in our case) // see exceptions.S for details mips32_setintctl(intCtl | INTCTL_VS_32); // interrupt enable, HW3 unmasked mips32_bissr (SR_IE | SR_HINT3); }
interrupt processing involves checking that it is caused by the presence of data in the incoming FIFO [ S14 ]:
// uart interrupt handlervoid__attribute__((interrupt, keep_interrupts_masked)) __mips_isr_hw3(){ // Receiver Data available interrupt handlerif(MFP_UART_IIR & MFP_UART_IIR_RDA) uartReceive(); }
followed by their reading (until the FIFO is empty) and the output [ S15 ]:
voiduartReceive(void){ // is there something in receiver fifo?while (MFP_UART_LSR & MFP_UART_LSR_DR) { // data receiveuint8_t data = MFP_UART_RXR; receivedDataOutput(data); #if RUNTYPE == HARDWARE uartTransmit(data); #endif } }
- if the work is done on hardware, then all received data is sent back [ S16 ]:
voiduartTransmit(uint8_t data){ // waiting for transmitter fifo emptywhile (!(MFP_UART_LSR & MFP_UART_LSR_TFE)); // data transmit MFP_UART_TXR = data; }
Below is the result of the program. Since when setting up the module we set the interrupt operation mode after receiving 4 characters, the transmission is temporarily interrupted for reception. The remaining characters were received by us after another similar interrupt, but which was already caused not by the presence of 4 characters in the queue, but not by an empty queue and timeout (the controller understood that there would be no more characters and informed that the queue was not empty);
for the 05_uart program, reception is already performed after the transmission is completed, all this time the received data is waiting in the FIFO of the receiver:
Acknowledgments
The author is grateful to the team of translators of the textbook David Harris and Sarah Harris “Digital Circuitry and Computer Architecture”, Imagination Technologies for the academic license for the modern processor core and personally Yuri Panchul YuriPanchul for his work on the popularization of MIPSfpga.
References
[L1] - Digital circuitry and computer architecture ;
[L2] - How to get started with MIPSfpga ;
[L3] - MIPSfpga-plus project on github ;
[L4] - Wikipedia: UART ;
[L5] - Wikipedia: UART16550 ;
[L6] - Google: UART16550 ;
[L7] - Project freecores / uart16550 ;
[L8] - Project olofk / uart16550 ;
[L9] - Project opencores / UART 16550 core ;
[L10] - FPGA Terasic DE10-Lite ;
[L11] - Project ahb_lite_uart16550 ;
[L12] -MIPSfpga and interrupts ;
Documentation
[D1] - UART IP Core Specification ;
[D2] - MIPS32 microAptiv UP Processor Core AHB-Lite Interface ;
Images and tables
[P1] - Example work on the debug board ;
[P2] - Work in the processing mode of the UART interrupt ;
[P3] - Work by periodically polling the register ;
Source Code Links
[S1] - Module mfp_ahb_lite_uart16550 ;
[S2] - Catalog mipsfpga-plus / uart16550 ;
[S3] - Option MFP_USE_DUPLEX_UART ;
[S4] - Signals UART_SRX and UART_STX ;
[S5] - Modem control interface ;
[S6] - Connection of the interrupt signal ;
[S7] - UART signals in simulation mode ;
[S8] - An example of using '05_uart' ;
[S9] - An example of using '08_uart_irq' ;
[S10] - Setting the operating mode of the example program ;
[S11] - The header file uart16550.h;
[S12] - Software settings UART16550 ;
[S13] - Setting interrupts ;
[S14] - UART interrupt processing ;
[S15] - Reading the received UART data ;
[S16] - Send back the received data ;