UART improved and added more reg read side effects

This commit is contained in:
bbracker 2021-06-10 09:53:48 -04:00
parent 3fb5f1df24
commit 58d0e46d02

View File

@ -94,7 +94,7 @@ module uartPC16550D(
logic rxfifodmaready; logic rxfifodmaready;
logic [8:0] rxdata9; logic [8:0] rxdata9;
logic [7:0] rxdata; logic [7:0] rxdata;
logic [15:0] rxerrbit, rxfullbit; logic [15:0] RXerrbit, rxfullbit;
// transmit data // transmit data
logic [11:0] TXHR, txdata, nexttxdata, txsr; logic [11:0] TXHR, txdata, nexttxdata, txsr;
@ -106,7 +106,9 @@ module uartPC16550D(
logic fifoenabled, fifodmamodesel, evenparitysel; logic fifoenabled, fifodmamodesel, evenparitysel;
// interrupts // interrupts
logic rxlinestatusintr, rxdataavailintr, modemstatusintr, intrpending, THRE, suppressTHREbecauseIIR, suppressTHREbecauseIIRtrig; logic RXerr, RXerrIP, squashRXerrIP, prevSquashRXerrIP, setSquashRXerrIP, resetSquashRXerrIP;
logic THRE, THRE_IP, squashTHRE_IP, prevSquashTHRE_IP, setSquashTHRE_IP, resetSquashTHRE_IP;
logic rxdataavailintr, modemstatusintr, intrpending;
logic [2:0] intrID; logic [2:0] intrID;
/////////////////////////////////////////// ///////////////////////////////////////////
@ -146,13 +148,15 @@ module uartPC16550D(
3'b111: SCR <= #1 Din; 3'b111: SCR <= #1 Din;
endcase endcase
end end
// Line Status Register (8.6.3) // Line Status Register (8.6.3)
// Ben 6/9/21 I don't like how this is a register. A lot of the individual bits have clocked components, so this just adds unecessary delay.
LSR[0] <= #1 rxdataready; // Data ready LSR[0] <= #1 rxdataready; // Data ready
if (RXBR[10]) LSR[1] <= #1 1; // overrun error LSR[1] <= #1 (LSR[1] | RXBR[10]) & ~squashRXerrIP;; // overrun error
if (RXBR[9]) LSR[2] <= #1 1; // parity error LSR[2] <= #1 (LSR[2] | RXBR[9]) & ~squashRXerrIP; // parity error
if (RXBR[8]) LSR[3] <= #1 1; // framing error LSR[3] <= #1 (LSR[3] | RXBR[8]) & ~squashRXerrIP; // framing error
if (rxbreak) LSR[4] <= #1 1; // break indicator LSR[4] <= #1 (LSR[4] | rxbreak) & ~squashRXerrIP; // break indicator
LSR[5] <= #1 THRE & ~(suppressTHREbecauseIIR | suppressTHREbecauseIIRtrig); // THRE (suppress trigger included to avoid 2-cycle delay) LSR[5] <= #1 THRE; // THRE
LSR[6] <= #1 ~txsrfull & THRE; // TEMT LSR[6] <= #1 ~txsrfull & THRE; // TEMT
if (rxfifohaserr) LSR[7] <= #1 1; // any bits in FIFO have error if (rxfifohaserr) LSR[7] <= #1 1; // any bits in FIFO have error
@ -162,7 +166,6 @@ module uartPC16550D(
MSR[2] <= #1 MSR[2] | (~RIb2 & RIbsync); // Trailing Edge of Ring Indicator MSR[2] <= #1 MSR[2] | (~RIb2 & RIbsync); // Trailing Edge of Ring Indicator
MSR[3] <= #1 MSR[3] | DCDb2 ^ DCDbsync; // Delta Data Carrier Detect MSR[3] <= #1 MSR[3] | DCDb2 ^ DCDbsync; // Delta Data Carrier Detect
end end
always_comb always_comb
if (~MEMRb) if (~MEMRb)
case (A) case (A)
@ -199,7 +202,6 @@ module uartPC16550D(
/////////////////////////////////////////// ///////////////////////////////////////////
// receive timing and control // receive timing and control
/////////////////////////////////////////// ///////////////////////////////////////////
always_ff @(posedge HCLK, negedge HRESETn) always_ff @(posedge HCLK, negedge HRESETn)
if (~HRESETn) begin if (~HRESETn) begin
rxoversampledcnt <= #1 0; rxoversampledcnt <= #1 0;
@ -260,7 +262,7 @@ module uartPC16550D(
if (rxstate == UART_DONE) begin if (rxstate == UART_DONE) begin
RXBR <= #1 {rxoverrunerr, rxparityerr, rxframingerr, rxdata}; // load recevive buffer register RXBR <= #1 {rxoverrunerr, rxparityerr, rxframingerr, rxdata}; // load recevive buffer register
if (fifoenabled) begin if (fifoenabled) begin
rxfifo[rxfifohead] <= #1 {rxoverrunerr, rxparityerr, rxframingerr, rxdata}; rxfifo[rxfifohead] <= #1 {rxoverrunerr, rxparityerr, rxframingerr, rxdata};
rxfifohead <= #1 rxfifohead + 1; rxfifohead <= #1 rxfifohead + 1;
end end
rxdataready <= #1 1; rxdataready <= #1 1;
@ -287,14 +289,14 @@ module uartPC16550D(
generate generate
genvar i; genvar i;
for (i=0; i<16; i++) begin for (i=0; i<16; i++) begin
assign rxerrbit[i] = |rxfifo[i][10:8]; // are any of the error conditions set? assign RXerrbit[i] = |rxfifo[i][10:8]; // are any of the error conditions set?
if (i > 0) if (i > 0)
assign rxfullbit[i] = ((rxfifohead==i) | rxfullbit[i-1]) & (rxfifotail != i); assign rxfullbit[i] = ((rxfifohead==i) | rxfullbit[i-1]) & (rxfifotail != i);
else else
assign rxfullbit[0] = ((rxfifohead==i) | rxfullbit[15]) & (rxfifotail != i); assign rxfullbit[0] = ((rxfifohead==i) | rxfullbit[15]) & (rxfifotail != i);
end end
endgenerate endgenerate
assign rxfifohaserr = |(rxerrbit & rxfullbit); assign rxfifohaserr = |(RXerrbit & rxfullbit);
// receive buffer register and ready bit // receive buffer register and ready bit
always_ff @(posedge HCLK, negedge HRESETn) // track rxrdy for DMA mode (FCR3 = FCR0 = 1) always_ff @(posedge HCLK, negedge HRESETn) // track rxrdy for DMA mode (FCR3 = FCR0 = 1)
@ -341,7 +343,6 @@ module uartPC16550D(
/////////////////////////////////////////// ///////////////////////////////////////////
// transmit holding register, shift register, FIFO // transmit holding register, shift register, FIFO
/////////////////////////////////////////// ///////////////////////////////////////////
always_comb begin // compute value for parity and tx holding register always_comb begin // compute value for parity and tx holding register
nexttxdata = fifoenabled ? txfifo[txfifotail] : TXHR; // pick from FIFO or holding register nexttxdata = fifoenabled ? txfifo[txfifotail] : TXHR; // pick from FIFO or holding register
case (LCR[1:0]) // compute parity from appropriate number of bits case (LCR[1:0]) // compute parity from appropriate number of bits
@ -420,20 +421,22 @@ module uartPC16550D(
/////////////////////////////////////////// ///////////////////////////////////////////
// interrupts // interrupts
/////////////////////////////////////////// ///////////////////////////////////////////
assign rxlinestatusintr = |LSR[4:1]; // LS interrupt if any of the flags are true assign RXerr = |LSR[4:1]; // LS interrupt if any of the flags are true
assign RXerrIP = RXerr & ~squashRXerrIP; // intr squashed upon reading LSR
assign rxdataavailintr = fifoenabled ? rxfifotriggered : rxdataready; assign rxdataavailintr = fifoenabled ? rxfifotriggered : rxdataready;
assign THRE = fifoenabled ? txfifoempty : ~txhrfull; assign THRE = fifoenabled ? txfifoempty : ~txhrfull;
assign THRE_IP = THRE & ~squashTHRE_IP; // THRE_IP squashed upon reading IIR
assign modemstatusintr = |MSR[3:0]; // set interrupt when modem pins change assign modemstatusintr = |MSR[3:0]; // set interrupt when modem pins change
// IIR: interrupt priority (Table 5) // IIR: interrupt priority (Table 5)
// set intrID based on highest priority pending interrupt source; otherwise, no interrupt is pending // set intrID based on highest priority pending interrupt source; otherwise, no interrupt is pending
always_comb begin always_comb begin
intrpending = 1; intrpending = 1;
if (rxlinestatusintr & IER[2]) intrID = 3'b011; if (RXerrIP & IER[2]) intrID = 3'b011;
else if (rxdataavailintr & IER[0]) intrID = 3'b010; else if (rxdataavailintr & IER[0]) intrID = 3'b010;
else if (rxfifotimeout & fifoenabled & IER[0]) intrID = 3'b110; else if (rxfifotimeout & fifoenabled & IER[0]) intrID = 3'b110;
else if (THRE & IER[1] & ~suppressTHREbecauseIIR) intrID = 3'b001; else if (THRE_IP & IER[1]) intrID = 3'b001;
else if (modemstatusintr & IER[3]) intrID = 3'b000; else if (modemstatusintr & IER[3]) intrID = 3'b000;
else begin else begin
intrID = 3'b000; intrID = 3'b000;
intrpending = 0; intrpending = 0;
@ -441,9 +444,16 @@ module uartPC16550D(
end end
always @(posedge HCLK) INTR <= #1 intrpending; // prevent glitches on interrupt pin always @(posedge HCLK) INTR <= #1 intrpending; // prevent glitches on interrupt pin
// Side effect of reading IIR is lowering THRE if most significant intr // Side effect of reading LSR is lowering overrun, parity, framing, break intr's
assign suppressTHREbecauseIIRtrig = ~MEMRb & (A==3'b010) & (intrID==2'h1); assign setSquashRXerrIP = ~MEMRb & (A==3'b101);
flopr #(1) suppressTHREreg(HCLK, (~HRESETn | (fifoenabled ? ~txfifoempty : txhrfull)), (suppressTHREbecauseIIRtrig | suppressTHREbecauseIIR), suppressTHREbecauseIIR); assign resetSquashRXerrIP = (rxstate == UART_DONE);
assign squashRXerrIP = (prevSquashRXerrIP | setSquashRXerrIP) & ~resetSquashRXerrIP;
flopr #(1) squashRXerrIPreg(HCLK, ~HRESETn, squashRXerrIP, prevSquashRXerrIP);
// Side effect of reading IIR is lowering THRE_IP if most significant intr
assign setSquashTHRE_IP = ~MEMRb & (A==3'b010) & (intrID==2'h1); // there's a 1-cycle delay on set squash so that THRE_IP doesn't change during the process of reading IIR (otherwise combinational loop)
assign resetSquashTHRE_IP = ~THRE;
assign squashTHRE_IP = prevSquashTHRE_IP & ~resetSquashTHRE_IP;
flopr #(1) squashTHRE_IPreg(HCLK, ~HRESETn, squashTHRE_IP | setSquashTHRE_IP, prevSquashTHRE_IP);
/////////////////////////////////////////// ///////////////////////////////////////////
// modem control logic // modem control logic