It has been a little over a month since I ported the open source module UART16550 to the AHB-Lite bus. Writing about it at that time was somewhat not logical, since the article about the MIPSfpga interrupts has not yet been published.
If you are an experienced developer, then only one useful news for you: UART16550 added to the MIPSfpga-plus system, you can not read further. And those who are interested in the disassembled example of using this module are welcome under cat.
It is assumed that the reader:
The history of the appearance of this chip is well described in [ L5 ], you can find documentation on it in google [ L6 ], something else is important for us:
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; else State <= 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 decision case(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 //io case(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
check that the following setting [ S3 ] is set in the file mfp_ahb_lite_matrix_config.vh:
`define MFP_USE_DUPLEX_UART
in the main.c file install [ 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
At startup, the UART16550 setup [ S12 ]:
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; }
setting interrupts [ S13 ] (detailed in [ L12 ]):
void mipsInterruptInit(void) { // Status.BEV 0 - vector interrupt mode mips32_bicsr (SR_BEV); // Cause.IV, 1 - special int vector (0x200) // where 0x200 - base for others interrupts; mips32_biscr (CR_IV); // get IntCtl reg value uint32_t intCtl = mips32_getintctl(); // set interrupt table vector spacing (0x20 in 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 precisely by the presence of data in the incoming FIFO [ S14 ]:
// uart interrupt handler void __attribute__ ((interrupt, keep_interrupts_masked)) __mips_isr_hw3 () { // Receiver Data available interrupt handler if(MFP_UART_IIR & MFP_UART_IIR_RDA) uartReceive(); }
followed by their reading (until the FIFO is empty) and the output [ S15 ]:
void uartReceive(void) { // is there something in receiver fifo? while (MFP_UART_LSR & MFP_UART_LSR_DR) { // data receive uint8_t data = MFP_UART_RXR; receivedDataOutput(data); #if RUNTYPE == HARDWARE uartTransmit(data); #endif } }
void uartTransmit(uint8_t data) { // waiting for transmitter fifo empty while (!(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 triggering mode after receiving 4 characters, the transmission is temporarily interrupted for reception. The remaining characters were received by us after another similar interruption, but which was already caused not by the presence of 4 characters in the queue, and not by an empty queue and timeout (the controller realized that there would be no more characters and informed that the queue was not empty);
for program 05_uart, reception is performed after the transfer is completed, all this time the received data is waiting in the receiver's FIFO:
The author is grateful to the team of translators of the textbook by David Harris and Sarah Harris “Digital Circuit Design and Computer Architecture”, by Imagination Technologies for the academic license for a modern processor core and personally for Yuri Panchul YuriPanchul for his work on promoting MIPSfpga.
[L1] - Digital circuit design and computer architecture ;
[L2] - How to start working 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] - Opencores / UART 16550 core project ;
[L10] - Terasic DE10-Lite FPGA board ;
[L11] - Project ahb_lite_uart16550 ;
[L12] - MIPSfpga and interrupts ;
[D1] - UART IP Core Specification ;
[D2] - MIPS32 microAptiv UP Processor Core AHB-Lite Interface ;
[P1] - Example operation on the debug board ;
[P2] - Work in UART interrupt handling mode ;
[P3] - Work by periodically polling the register ;
[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] - Interrupt signal connection ;
[S7] - UART signals in simulation mode ;
[S8] - An example of the use of '05_uart' ;
[S9] - An example of the use of '08_uart_irq' ;
[S10] - Setting the mode of the example program ;
[S11] - Header file uart16550.h ;
[S12] - Software setting UART16550 ;
[S13] - Setting interrupts ;
[S14] - UART interrupt handling ;
[S15] - Reading received UART data ;
[S16] - Return sending of received data ;
Source: https://habr.com/ru/post/325168/
All Articles