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.


    image


    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 ]:

    mfp_ahb_lite_uart16550
    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);


      image


    • 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:


      image



    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 ;


    Also popular now: