From 8ec97abe6ef518a367a16d28c5cec150023691fa Mon Sep 17 00:00:00 2001 From: Roberto Hexsel <roberto@inf.ufpr.br> Date: Tue, 18 Apr 2017 09:32:09 -0300 Subject: [PATCH] several fixes: remote now reads 1st line of serial.inp; UART.status.RX flags updated on a DATA-read, UART.status.TX updated on a status-read; improvements to UART interrupt handler --- cMIPS/include/handlers.s | 49 ++++++++------ cMIPS/include/start.s | 3 +- cMIPS/tests/nullifies.expected | 7 +- cMIPS/tests/nullifies.s | 117 +++++++++++++++++++++++++++++---- cMIPS/tests/uart_defs.c | 2 + cMIPS/tests/uart_irx.c | 26 +++++--- cMIPS/tests/uart_irx.expected | 1 + cMIPS/tests/uartrx.c | 9 ++- cMIPS/tests/uartrx.expected | 1 + cMIPS/tests/uarttx.c | 7 +- cMIPS/tests/uarttx.expected | Bin 50 -> 46 bytes cMIPS/vhdl/uart.vhd | 64 +++++++++--------- 12 files changed, 203 insertions(+), 83 deletions(-) diff --git a/cMIPS/include/handlers.s b/cMIPS/include/handlers.s index 48121cf..f6e4f31 100644 --- a/cMIPS/include/handlers.s +++ b/cMIPS/include/handlers.s @@ -57,9 +57,6 @@ extCounter: # lw $a1, 1*4($k1) #---------------------------------- - mfc0 $k0, c0_status # Read STATUS register - ori $k0, $k0, M_StatusIEn # but do not modify its contents - mtc0 $k0, c0_status # except for re-enabling interrupts eret # Return from interrupt .end extCounter #---------------------------------------------------------------- @@ -72,6 +69,15 @@ extCounter: .bss .align 2 .global Ud + + .equ RXHD,0 + .equ RXTL,4 + .equ RX_Q,8 + .equ TXHD,24 + .equ TXTL,28 + .equ TXQ,32 + .equ NRX,48 + .equ NTX,52 Ud: rx_hd: .space 4 # reception queue head index rx_tl: .space 4 # tail index @@ -83,18 +89,34 @@ nrx: .space 4 # characters in RX_queue ntx: .space 4 # spaces left in TX_queue _uart_buff: .space 16*4 # up to 16 registers to be saved here - # _uart_buff[0]=UARTstatus, [1]=UARTcontrol, [2]=data_inp, [3]=new, + # _uart_buff[0]=UARTstatus, [1]=UARTcontrol, [2]=$v0, [3]=$v1, # [4]=$ra, [5]=$a0, [6]=$a1, [7]=$a2, [8]=$a3 .set UART_rx_irq,0x08 .set UART_tx_irq,0x10 + .equ UCTRL,0 # UART registers + .equ USTAT,0 + .equ UDATA,4 + .text .set noreorder .global UARTinterr .ent UARTinterr UARTinterr: + + #---------------------------------------------------------------- + # While you are developing the complete handler, uncomment the + # line below + # + # .include "../tests/handlerUART.s" + # + # Your new handler should be self-contained and do the + # return-from-exception. To do that, copy the lines below up + # to, but excluding, ".end UARTinterr", to yours handlerUART.s. + #---------------------------------------------------------------- + lui $k0, %hi(_uart_buff) # get buffer's address ori $k0, $k0, %lo(_uart_buff) @@ -105,23 +127,17 @@ UARTinterr: lui $a0, %hi(HW_uart_addr)# get device's address ori $a0, $a0, %lo(HW_uart_addr) - lw $k1, 0($a0) # Read status, remove interrupt request + lw $k1, USTAT($a0) # Read status, remove interrupt request sw $k1, 0*4($k0) # and save UART status to memory - #---------------------------------- - # while you are developing the complete handler, - # uncomment the line below and comment out lines up to UARTret - # .include "../tests/handlerUART.s" - #---------------------------------- - andi $a1, $k1, UART_rx_irq # Is this reception? beq $a1, $zero, UARTret # no, ignore it and return nop # handle reception - lw $a1, 4($a0) # Read data from device + lw $a1, UDATA($a0) # Read data from device - lui $a2, %hi(Ud) # get address for data & flag + lui $a2, %hi(Ud) # get address for data & flag ori $a2, $a2, %lo(Ud) sw $a1, 0*4($a2) # and return from interrupt @@ -133,9 +149,6 @@ UARTret: lw $a1, 6*4($k0) # restore registers $a0,$a1, others? lw $a0, 5*4($k0) - mfc0 $k0, c0_status # Read STATUS register - ori $k0, $k0, M_StatusIEn # but do not modify its contents - mtc0 $k0, c0_status # except for re-enabling interrupts eret # Return from interrupt .end UARTinterr #---------------------------------------------------------------- @@ -153,10 +166,6 @@ countCompare: addiu $k1,$k1,num_cycles # set next interrupt in so many ticks mtc0 $k1,c0_compare # write to COMPARE to clear IRQ - mfc0 $k0, c0_status # Read STATUS register - ori $k0, $k0, M_StatusIEn # but do not modify its contents - mtc0 $k0, c0_status # except for re-enabling interrupts - ehb eret # Return from interrupt .end countCompare #---------------------------------------------------------------- diff --git a/cMIPS/include/start.s b/cMIPS/include/start.s index d324172..52875b1 100644 --- a/cMIPS/include/start.s +++ b/cMIPS/include/start.s @@ -321,7 +321,7 @@ _excp_0200: nop - ## the code for each handler must repeat the exception return + ## the code for each handler must contain an exception return ## sequence shown below in excp_0200ret. handlers_tbl: j dismiss # no request: 000 @@ -354,7 +354,6 @@ _excp_0200ret: mtc0 $k0, c0_status # else keep as it was on int entry ehb eret # Return from interrupt - nop .end _excp_0200 #---------------------------------------------------------------- diff --git a/cMIPS/tests/nullifies.expected b/cMIPS/tests/nullifies.expected index 9bcd9b0..de0e14a 100644 --- a/cMIPS/tests/nullifies.expected +++ b/cMIPS/tests/nullifies.expected @@ -1,3 +1,6 @@ -00000540 -08802000 + 00000000 +ok + +ok + diff --git a/cMIPS/tests/nullifies.s b/cMIPS/tests/nullifies.s index dc4b961..7e89f05 100644 --- a/cMIPS/tests/nullifies.s +++ b/cMIPS/tests/nullifies.s @@ -22,6 +22,7 @@ .equ numCy,0xc0000000 # enable counter .equ PRINT,$15 + .equ STDOUT,$17 .equ COUNT,$16 .equ NL,$13 @@ -84,21 +85,21 @@ _excp_180: # interrupt handler ------------------------------------------------ # ## - ## stop the counter, print EPC and CAUSE, and return + ## stop the counter, print EPC, and return ## .org x_EXCEPTION_0200,0 _excp_200: - sw $zero, 0(COUNT) # stop the counter + lw $k0, 0(COUNT) # read the counter and disable counting + la $k1, 0x3ffffff + and $k0, $k1, $k0 + sw $k0, 0(COUNT) # stop the counter - mfc0 $k1, c0_epc # read EPC - sw $k1, 0(PRINT) - addi $k1, $k1, 4 # skip interrupted instruction - nop - mtc0 $k1, c0_epc # write new EPC - - mfc0 $k1, c0_cause # read CAUSE - sw $k1, 0(PRINT) + mfc0 $k1, c0_epc # read EPC -- this is a "return value", keep it! + # sw $k1, 0(PRINT) + addi $k0, $k1, 4 # skip interrupted instruction nop + mtc0 $k0, c0_epc # write new EPC + ehb eret # @@ -122,10 +123,11 @@ _excp_BFC0: ## .org x_ENTRY_POINT,0 main: la PRINT, x_IO_BASE_ADDR + la STDOUT, (x_IO_BASE_ADDR + 1 * x_IO_ADDR_RANGE) la COUNT, HW_counter_addr li NL, '\n' - + sw NL, 0(STDOUT) # print a new line ## ## counter will interrupt right on the MULT instruction ## MULT is cancelled by the interrupt @@ -145,15 +147,106 @@ main: la PRINT, x_IO_BASE_ADDR nop # counter starts counting nop nop - mult $20, $21 # interrupts on the 4th cycle +_mult: mult $20, $21 # interrupts on the 4th cycle # this MULT is cancelled by the handler mflo $22 sw $22, 0(PRINT) # should print zero + la $4, _mult + nop + nop + bne $4, $k1, _err1 # error if EPC != _mult + nop + + jal ok + nop + + nop # clear out the pipeline + nop + nop + nop + nop + + + ## + ## counter will interrupt right on the MTC0 instruction + ## the MTC0 disables the interrupts + ## MTC0 is NOT cancelled by the interrupt + ## handler skips the interrupted instruction + ## STATUS.IE must be zero and interrupt is cancelled + ## + li $6, c0_status_reset # RESET, kernel mode, all else disabled + + li $5, (numCy+4) # interrupt in 4+4 cycles + sw $5, 0(COUNT) # it takes four cycles to start counting + nop # 4 pipestages + nop + nop + + nop # counter starts counting + # change to STATUS must be in EXEC pipestage +_mtc0: mtc0 $6, c0_status # this MUST NOT be cancelled by the interrupt + nop + nop + lw $7, 0(COUNT) # was the IRQ taken? If so, + la $4, (numCy+4) # value in count must be 4 (b31b30=00) + nop + nop + bne $4, $7, _err2 # error if COUNTER != 4 + nop + + jal ok + nop here: j exit nop + +_err1: # interrupt was on the wrong instruction + li $30, 'n' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'o' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 't' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'M' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'U' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'L' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'T' + sw $30, x_IO_ADDR_RANGE($15) + j exit + sw NL, x_IO_ADDR_RANGE($15) # print a newline + +_err2: # interrupt was on the wrong instruction + li $30, 'w' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'a' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 's' + sw $30, x_IO_ADDR_RANGE($15) + li $30, ' ' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'I' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'R' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'Q' + sw $30, x_IO_ADDR_RANGE($15) + j exit + sw NL, x_IO_ADDR_RANGE($15) # print a newline + + + # nothing wrong +ok: li $30, 'o' + sw $30, x_IO_ADDR_RANGE($15) + li $30, 'k' + sw $30, x_IO_ADDR_RANGE($15) + sw $13, x_IO_ADDR_RANGE($15) # print a newline + jr $ra + sw $13, x_IO_ADDR_RANGE($15) # print a newline diff --git a/cMIPS/tests/uart_defs.c b/cMIPS/tests/uart_defs.c index 699e596..249cc4c 100644 --- a/cMIPS/tests/uart_defs.c +++ b/cMIPS/tests/uart_defs.c @@ -35,3 +35,5 @@ typedef struct serial { Tdata d; // address is (int *)(IO_UART_ADDR+1) } Tserial; + +#define EOT 0x04 // End Of Transmission character diff --git a/cMIPS/tests/uart_irx.c b/cMIPS/tests/uart_irx.c index a0c00fc..a2b2bc4 100644 --- a/cMIPS/tests/uart_irx.c +++ b/cMIPS/tests/uart_irx.c @@ -4,10 +4,15 @@ // Remote unit reads string from serial.inp and sends it over the // serial line. // +// This program makes use of abstraction to synchronize handler and main(). +// handler sets a flag on receiving a new character; main() waits for +// flag==1, reads char, makes flag=0. +// // UARTinterr, in include/handlers.s, reads newly arrived character, -// sets a flag and puts character in a buffer. main() reads from the -// buffer and prints it to the simulator's standard output. - +// sets a flag and puts character in a buffer. +// main() waits for flag==1, then reads from the buffer, prints +// character to the simulator's standard output, makes flag=0. +// #include "cMIPS.h" @@ -36,7 +41,7 @@ int main(void) { // receive a string through the UART serial interface ctrl.speed = SPEED; uart->cs.ctl = ctrl; // initizlize UART - // handler sets flag=bfr[3] to 1 after new character is received; + // handler sets flag=[U_FLAg] to 1 after new character is received; // this program resets the flag on fetching a new character from buffer bfr[U_FLAG] = 0; // reset flag @@ -48,13 +53,16 @@ int main(void) { // receive a string through the UART serial interface uart->cs.ctl = ctrl; do { - while ( (c = (char)bfr[U_FLAG]) == 0 ) // check flag in Ud[1] - {}; // nothing new + while ( (c = (char)bfr[U_FLAG]) == 0 ) // check flag in Ud[] + delay_cycle(1); // nothing new, wait c = (char)bfr[U_DATA]; // get new character bfr[U_FLAG] = 0; // and reset flag - to_stdout( (int)c ); // and print new char - } while (c != '\0'); // end of string? + if (c != EOT) + to_stdout( c ); // and print new char + else + to_stdout( '\n' ); // and print new-line + } while (c != EOT); // end of transmission? - return c; + return c; // so compiler won't optimize away the last loop } diff --git a/cMIPS/tests/uart_irx.expected b/cMIPS/tests/uart_irx.expected index f1de9d7..56f4ada 100644 --- a/cMIPS/tests/uart_irx.expected +++ b/cMIPS/tests/uart_irx.expected @@ -1,3 +1,4 @@ + abcdef 012345 diff --git a/cMIPS/tests/uartrx.c b/cMIPS/tests/uartrx.c index b8d0b63..a783eff 100644 --- a/cMIPS/tests/uartrx.c +++ b/cMIPS/tests/uartrx.c @@ -51,11 +51,14 @@ int main(void) { // receive a string through the UART serial interface i = i+1; while ( (state = (int)uart->cs.stat.rxFull) == 0 ) - if (state == 0) cmips_delay(1); // just do something with state + delay_cycle(1); // just do something s[i] = (char)uart->d.rx; - to_stdout( s[i] ); + if (s[i] != EOT) + to_stdout( s[i] ); // and print new char + else + to_stdout( '\n' ); // and print new-line - } while (s[i] != '\0'); + } while (s[i] != EOT); return(state); diff --git a/cMIPS/tests/uartrx.expected b/cMIPS/tests/uartrx.expected index f1de9d7..56f4ada 100644 --- a/cMIPS/tests/uartrx.expected +++ b/cMIPS/tests/uartrx.expected @@ -1,3 +1,4 @@ + abcdef 012345 diff --git a/cMIPS/tests/uarttx.c b/cMIPS/tests/uarttx.c index 7aace2b..f3fa173 100644 --- a/cMIPS/tests/uarttx.c +++ b/cMIPS/tests/uarttx.c @@ -69,17 +69,16 @@ int main(void) { // send a string through the UART serial interface i = i+1; while ( (state = (int)uart->cs.stat.txEmpty) == 0 ) - {}; // if (state == 1) cmips_delay(2); // just do something with state + delay_cycle(1); // do something uart->d.tx = (int)s[i]; } while (s[i] != '\0'); // '\0' is transmitted in previous line - // then wait until last char is sent out of the shift-register to return startCounter(COUNTING, 0); while ( (val=(readCounter() & 0x3fffffff)) < COUNTING ) - {}; + delay_cycle(1); - return val; // so compiler won't optimize away the last loop + return val; // so compiler won't optimize away the last loop } diff --git a/cMIPS/tests/uarttx.expected b/cMIPS/tests/uarttx.expected index 55747baac2bce6b6340fa2ffd40bfe2386392a75..553acfcc0dc12653edd2285e4e73b210c8c80be6 100644 GIT binary patch delta 5 McmXrgo5-jK00Wu<ApigX delta 10 RcmdNhV&s~j$IQjR1po+O0YLx& diff --git a/cMIPS/vhdl/uart.vhd b/cMIPS/vhdl/uart.vhd index efd0fc2..17cb5de 100644 --- a/cMIPS/vhdl/uart.vhd +++ b/cMIPS/vhdl/uart.vhd @@ -26,10 +26,10 @@ -- 001: 1/8 CPU clock rate -- for VHDL/C debugging only -- 010: 1/16 CPU clock rate -- for VHDL/C debugging only -- 011: 1/32 CPU clock rate -- for VHDL/C debugging only --- 100: 230.400 baud --- 101: 115.200 baud --- 110: 19.200 baud --- 111: 9.600 baus +-- 100: 230.400 bits per second +-- 101: 115.200 bits per second +-- 110: 19.200 bits per second +-- 111: 9.600 bits per second -- b3=1: signal interrupt on RX buffer full, when a new octet is available -- b4=1: signal interrupt on TX buffer empty, when TX space is available -- b5,b6: ignored, not used @@ -44,7 +44,9 @@ -- b5: receive buffer is full -- b6: transmit buffer is empty -- b7: Clear to Send (CTS) is active --- when CPU reads status register bits 0,1,3,4 are cleared +-- +-- when CPU reads from RXdat register, bits 0,1,3 are cleared +-- when CPU reads from STATUS register, bit 4 is cleared -- -- RX and TX circuits are dobule-buffered @@ -124,7 +126,7 @@ architecture estrutural of uart_int is signal rx_ld, rx_shift, rx_next, rx_bfr_full : std_logic; signal rxdat_1to0, rxdat_new, rxdat_int, rxdat_old : std_logic; signal rxclk_fall, rxclk_rise, en_rx_clk, rx_done, rxclk : std_logic; - signal a_overrun, a_framing, sel_delayed, reset_rxck : std_logic; + signal a_overrun, a_framing, reset_rxck, s_stat_dly : std_logic; signal sta_xmit_sto, sta_recv_sto : reg10; signal err_overrun, err_framing : std_logic; signal rx_int_set, interr_RX_full, tx_int_set, interr_TX_empty : std_logic; @@ -150,8 +152,6 @@ begin interr <= interr_TX_empty or interr_RX_full; - U_delay: FFDsimple port map (clk, rst, s_stat, sel_delayed); - -- TRANSMISSION =========================================================== -- txreg is updated under the assumption that SW checked TXempty beforehand U_txreg: register8 port map (clk,rst, s_tx, d_inp(7 downto 0), txreg); @@ -162,8 +162,10 @@ begin U_transmit: par_ser10 port map (clk, rst, tx_ld, tx_next, sta_xmit_sto, txdat); + U_STAT_DELAY: FFDsimple port map (clk, rst, s_stat, s_stat_dly); + tx_int_set <= ctrl(4) and tx_ld; - d_int_tx_empty <= (interr_TX_empty or tx_int_set) and not(sel_delayed); + d_int_tx_empty <= (interr_TX_empty or tx_int_set) and not(s_stat_dly); U_tx_int: FFDsimple port map (clk, rst, d_int_tx_empty, interr_TX_empty); @@ -360,14 +362,14 @@ begin (sta_recv_sto(9) /= '1' or sta_recv_sto(0)/='0') ) else '0'; - d_err_framing <= (a_framing or err_framing) and not(sel_delayed); + d_err_framing <= (a_framing or err_framing) and not(s_rx); U_framing: FFDsimple port map (clk, rst, d_err_framing, err_framing); - d_err_overrun <= (a_overrun or err_overrun) and not(sel_delayed); + d_err_overrun <= (a_overrun or err_overrun) and not(s_rx); U_overrun: FFDsimple port map (clk, rst, d_err_overrun, err_overrun); rx_int_set <= ctrl(3) and rx_done; - d_rx_int_set <= (rx_int_set or interr_RX_full) and not(sel_delayed); + d_rx_int_set <= (rx_int_set or interr_RX_full) and not(s_rx); U_rx_int: FFDsimple port map (clk, rst, d_rx_int_set, interr_RX_full); @@ -778,6 +780,7 @@ entity remota is inpDat : in std_logic; -- serial input outDat : out std_logic; -- serial output bit_rt : in reg3); -- selects bit rate + constant EOT : reg8 := x"04"; -- end of transmission character end remota; architecture behavior of remota is @@ -836,10 +839,10 @@ begin tx_dbg_st <= integer(tx_state'pos(tx_current_st)); -- debugging only - U_tx: process (tx_current_st, start) + U_tx: process (tx_current_st, start, rst) variable sentence : line; variable char : character; - variable good, send_null : boolean; + variable good, send_eot : boolean; variable bfr : reg8; variable j : integer; begin @@ -848,13 +851,13 @@ begin when st_wait => -- 12 wait for starting signal outDat <= '1'; tx_run <= '0'; -- hold TX clock - send_null := FALSE; - if start = '0' then + send_eot := FALSE; + if start = '0' then tx_next_st <= st_wait; else - if not endfile(input_stream) then + if not endfile(input_stream) and rst = '1' then readline( input_stream, sentence ); -- read first line of text - -- assert false report "fst line: "&integer'image(sentence'length); + assert TRUE report "fst line: "&integer'image(sentence'length); j := 1; tx_next_st <= st_init; else @@ -869,15 +872,15 @@ begin if not endfile(input_stream) then if j > sentence'right then -- read new line of input readline( input_stream, sentence ); - -- assert false report "new line: "&integer'image(sentence'length); + assert TRUE report "new line: "&integer'image(sentence'length); bfr := x"0a"; -- new line j := 0; elsif sentence'length = 0 then bfr := x"0a"; -- send new line for empty line - -- assert false report "empty line: " & integer'image(j)&" " & LF; + assert TRUE report "empty line: " & integer'image(j)&" " & LF; else read (sentence, char, good); - -- assert false report "read: " & integer'image(j) & " " &char; + assert TRUE report "read: " & integer'image(j) & " " &char; bfr := std_logic_vector(to_signed( character'pos(char), 8)); end if; tx_next_st <= st_start; @@ -916,12 +919,12 @@ begin outDat <= '1'; tx_next_st <= st_idle; when st_done => -- 13 wait forever - if send_null = FALSE then - bfr := x"00"; -- send out a NULL character - send_null := TRUE; + if send_eot = FALSE then + bfr := EOT; -- send out an END-OF-TRANSMISSION + send_eot := TRUE; tx_next_st <= st_start; else - tx_next_st <= st_done; -- no more input, done! + tx_next_st <= st_done; -- no more input, wait forever outDat <= '1'; end if; tx_run <= '0'; -- stop clock @@ -968,11 +971,7 @@ begin rx_next_st <= st_start; when st_start => reset_rxck <= '0'; - -- if rising_edge(rx_clk) then rx_next_st <= st_b0; - -- else - -- rx_next_st <= st_start; - -- end if; when st_b0 => if falling_edge(rx_clk) then recv(0) <= inpDat; @@ -1039,8 +1038,11 @@ begin rx_run <= '0'; rx_next_st <= st_idle; - write ( msg, character'val(to_integer( unsigned(recv))) ); - if recv = x"00" or recv = x"0a" then + if ((recv /= x"0a") and (recv /= x"04")) then + write ( msg, character'val(to_integer(unsigned(recv))) ); + end if; + + if ((recv = x"0a") or (recv = x"04")) then writeline( output_stream, msg ); end if; -- GitLab