diff --git a/debugger/UART.bsv b/debugger/UART.bsv new file mode 100644 index 0000000..2ea1ac4 --- /dev/null +++ b/debugger/UART.bsv @@ -0,0 +1,243 @@ +package UART; + +import Cntrs::*; +import GetPut::*; +import FIFOF::*; +import SpecialFIFOs::*; +import StmtFSM::*; +import Connectable::*; + +import PinSync::*; +import GlitchFilter::*; +import Strobe::*; + +(* always_enabled *) +interface UART_RX_PHY; + (* prefix="" *) + method Action rx_in((* port="rx_in" *) bit b); + (* result="cts" *) + method Bool stop_sending(); +endinterface + +(* always_enabled *) +interface UART_TX_PHY; + (* result="tx_out" *) + method bit tx_out(); + (* prefix="" *) + method Action can_send((* port="rts" *) Bool send); +endinterface + +(* always_enabled *) +interface UART_PHY; + (* prefix="" *) + method Action rx_in((* port="rx_in" *) bit b); + (* result="tx_out" *) + method bit tx_out(); + (* result="cts" *) + method Bool stop_sending(); + (* prefix="" *) + method Action can_send((* port="rts" *) Bool send); +endinterface + +interface UART_RX; + interface UART_RX_PHY phy; + interface Get#(Bit#(8)) receive; +endinterface + +interface UART_TX; + interface UART_TX_PHY phy; + interface Put#(Bit#(8)) send; +endinterface + +interface UART; + interface UART_PHY phy; + interface Put#(Bit#(8)) send; + interface Get#(Bit#(8)) receive; +endinterface + +typedef enum { + WaitIdle, + Idle, + Read, + Stop, + Cork +} RXState deriving (Bits, Eq); + +module mkUARTReceiver(Integer clock_frequency, Integer uart_bitrate, UART_RX ifc); + Reg#(bit) rx_sync <- mkPinSync(0); + let rx_in <- mkGlitchFilter(3, 0); + mkConnection(toGet(asReg(rx_sync)), toPut(asReg(rx_in))); + + Reg#(RXState) rx_state <- mkReg(WaitIdle); + Strobe bit_16x_strobe <- mkStrobe(clock_frequency, 16*uart_bitrate); + Count#(UInt#(4)) cnt <- mkCount(0); + Reg#(Bit#(8)) shift_in <- mkReg(0); + FIFOF#(Bit#(8)) rx <- mkBypassFIFOF(); + + (* no_implicit_conditions, fire_when_enabled *) + rule rx_counter (bit_16x_strobe); + cnt.incr(1); + endrule + + (* no_implicit_conditions, fire_when_enabled *) + rule rx_wait_idle (rx_state == WaitIdle && bit_16x_strobe && rx_in == 1); + rx_state <= Idle; + endrule + + (* no_implicit_conditions, fire_when_enabled *) + rule rx_idle (rx_state == Idle && bit_16x_strobe && rx_in == 0); + rx_state <= Read; + cnt <= 1; + shift_in <= 8'hFF; + endrule + + (* no_implicit_conditions, fire_when_enabled *) + rule rx_read (rx_state == Read && bit_16x_strobe && cnt == 7); + let shifted_out = shift_in[0]; + shift_in <= {rx_in, shift_in[7:1]}; + if (shifted_out == 0) begin // start bit reached end of shiftreg + rx_state <= Stop; + end + endrule + + (* fire_when_enabled *) + rule rx_stop (rx_state == Stop); + if (rx.notFull) begin + rx.enq(shift_in); + rx_state <= WaitIdle; + end + else + rx_state <= Cork; + endrule + + (* fire_when_enabled *) + rule rx_cork (rx_state == Cork); + rx.enq(shift_in); + rx_state <= WaitIdle; + endrule + + interface UART_RX_PHY phy; + method rx_in = rx_sync._write; + method Bool stop_sending(); + // Signal is active low, so 1 == "stop sending" + return rx_state == Cork; + endmethod + endinterface + + interface receive = toGet(rx); +endmodule + +typedef enum { + Idle, + Ready, + Send +} TXState deriving (Bits, Eq); + +module mkUARTTransmitter(Integer clock_frequency, Integer uart_bitrate, UART_TX ifc); + Reg#(bit) cts_sync <- mkPinSync(0); + let cts_in <- mkGlitchFilter(3, 0); + mkConnection(toGet(asReg(cts_sync)), toPut(asReg(cts_in))); + + Reg#(TXState) tx_state <- mkReg(Idle); + Strobe bit_strobe <- mkStrobe(clock_frequency, uart_bitrate); + Reg#(UInt#(4)) cnt <- mkReg(0); + Reg#(Bit#(9)) shift_out <- mkReg(9'h1FF); + FIFOF#(Bit#(8)) tx <- mkPipelineFIFOF(); + + (* fire_when_enabled *) + rule tx_idle (tx_state == Idle && tx.notEmpty); + shift_out <= {tx.first, 0}; + tx.deq(); + tx_state <= Ready; + bit_strobe.reset(); + endrule + + (* no_implicit_conditions, fire_when_enabled *) + rule rx_ready (tx_state == Ready && bit_strobe && cts_in == 1); + tx_state <= Send; + cnt <= 0; + endrule + + (* no_implicit_conditions, fire_when_enabled *) + rule tx_send (tx_state == Send && bit_strobe); + shift_out <= {1'b1, shift_out[8:1]}; + cnt <= cnt+1; + if (cnt == 9) + tx_state <= Idle; + endrule + + interface UART_TX_PHY phy; + method bit tx_out(); + if (tx_state == Send) + return shift_out[0]; + else + return 1'b1; + endmethod + method Action can_send(b); + cts_sync <= pack(b); + endmethod + endinterface + + interface Put send = toPut(tx); +endmodule + +module mkUART(Integer clock_frequency, Integer uart_bitrate, UART ifc); + let _rx <- mkUARTReceiver(clock_frequency, uart_bitrate); + let _tx <- mkUARTTransmitter(clock_frequency, uart_bitrate); + + interface UART_PHY phy; + method rx_in = _rx.phy.rx_in; + method tx_out = _tx.phy.tx_out; + method stop_sending = _rx.phy.stop_sending; + method can_send = _tx.phy.can_send; + endinterface + interface send = _tx.send; + interface receive = _rx.receive; +endmodule + +typeclass FlowControlled#(type ifc); + module disableFlowControl(ifc i, Empty ret); +endtypeclass + +instance FlowControlled#(UART_RX_PHY); + module disableFlowControl(UART_RX_PHY phy, Empty ifc); + endmodule +endinstance + +instance FlowControlled#(UART_TX_PHY); + module disableFlowControl(UART_TX_PHY phy, Empty ifc); + (* no_implicit_conditions,fire_when_enabled *) + rule always_send; + phy.can_send(True); + endrule + endmodule +endinstance + +instance FlowControlled#(UART_PHY); + module disableFlowControl(UART_PHY phy, Empty ifc); + (* no_implicit_conditions,fire_when_enabled *) + rule always_send; + phy.can_send(True); + endrule + endmodule +endinstance + +instance FlowControlled#(UART_RX); + module disableFlowControl(UART_RX rx, Empty ifc); + disableFlowControl(rx.phy); + endmodule +endinstance + +instance FlowControlled#(UART_TX); + module disableFlowControl(UART_TX tx, Empty ifc); + disableFlowControl(tx.phy); + endmodule +endinstance + +instance FlowControlled#(UART); + module disableFlowControl(UART uart, Empty ifc); + disableFlowControl(uart.phy); + endmodule +endinstance + +endpackage diff --git a/debugger/UART_Test.bsv b/debugger/UART_Test.bsv new file mode 100644 index 0000000..0cc5b60 --- /dev/null +++ b/debugger/UART_Test.bsv @@ -0,0 +1,265 @@ +package UART_Test; + +import Assert::*; +import StmtFSM::*; +import Connectable::*; +import GetPut::*; +import Probe::*; + +import UART::*; +import Testing::*; +import Strobe::*; + +interface Test; + method Action start(); + method Bool done(); +endinterface + +module mkTestReceiver(Test); + Reg#(Bool) running <- mkReg(False); + + let testflags <- mkTestFlags(); + let cycles <- mkCycleCounter(); + + let dut <- mkUARTReceiver(25_000_000, 115_200); + Reg#(bit) tx <- mkReg(1); + mkConnection(toGet(asReg(tx)), toPut(dut.phy.rx_in)); + + Probe#(Bit#(8)) read_probe <- mkProbe(); + Probe#(Bool) cork_probe <- mkProbe(); + rule record_cork; + cork_probe <= dut.phy.stop_sending(); + endrule + + Reg#(Bit#(40)) shift[2] <- mkCReg(2, 40'hFFFFFFFFFF); + let shift_in_strobe <- mkStrobe(25_000_000, 115_200); + (* no_implicit_conditions, fire_when_enabled *) + rule shift_in (running && shift_in_strobe && (tx == 0 || !dut.phy.stop_sending)); + if (testflags.verbose) + $display("%0d (%0d): to UART: %0d", cycles.all, cycles, shift[0][0]); + tx <= shift[0][0]; + shift[0] <= {1'b1, shift[0][39:1]}; + endrule + + function Action start_tx(Bit#(40) val); + return action + shift[1] <= val; + endaction; + endfunction + + function Bool tx_idle(); + return shift[0] == 40'hFFFFFFFFFF; + endfunction + + function Bool tx_is(Bit#(40) val); + return shift[0] == val; + endfunction + + let fsm <- mkFSM(seq + running <= True; + // Let UART initialize and settle + repeat (61) noAction; + // desynchronize sender and receiver strobes, to emulate a + // real setup. + shift_in_strobe.reset(); + + action + dynamicAssert(tx_idle(), "transmitter not idle"); + dynamicAssert(!dut.phy.stop_sending(), "receiver not ready to receive"); + endaction + + // Single byte transmission + action + start_tx({10'h3FF, 10'h3FF, 10'h3FF, 1'b1, 8'd77, 1'b0}); + cycles.reset(); + endaction + + // Read received byte + action + let got <- dut.receive.get(); + read_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): UART.rx = %0d, want %0d", cycles.all, cycles, got, 77); + dynamicAssert(got == 77, "wrong byte received"); + dynamicAssert(cycles < 2100, "byte not received during stop bit"); + endaction + dynamicAssert(!dut.phy.stop_sending(), "receiver not ready to receive"); + + await(shift_in_strobe); + action + dynamicAssert(tx_idle(), "transmitter not idle"); + dynamicAssert(!dut.phy.stop_sending(), "receiver not ready to receive"); + endaction + await(shift_in_strobe); + + // Send 4 bytes back to back, to check flow control + action + start_tx({ + 1'b1, 8'd25, 1'b0, + 1'b1, 8'd14, 1'b0, + 1'b1, 8'd59, 1'b0, + 1'b1, 8'd42, 1'b0}); + cycles.reset(); + endaction + + // Once two bytes are sent, the receiver should cork. + await(tx_is({10'h3FF, 10'h3FF, 1'b1, 8'd25, 1'b0, 1'b1, 8'd14, 1'b0})); + repeat (1000) noAction; + dynamicAssert(dut.phy.stop_sending(), "receiver did not cork sender"); + + // Read out one byte, verify that one more byte can transmit. + action + let got <- dut.receive.get(); + read_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): UART.rx = %0d, want %0d", cycles.all, cycles, got, 42); + dynamicAssert(got == 42, "wrong byte received"); + dynamicAssert(cycles < 5400, "byte not received during stop bit"); + endaction + await(tx_is({10'h3FF, 10'h3FF, 10'h3FF, 1'b1, 8'd25, 1'b0})); + dynamicAssert(dut.phy.stop_sending(), "receiver did not cork sender"); + + // Read out one more byte, check the final byte transmits. + action + let got <- dut.receive.get(); + read_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): UART.rx = %0d, want %0d", cycles.all, cycles, got, 59); + dynamicAssert(got == 59, "wrong byte received"); + dynamicAssert(cycles < 7600, "byte not received during stop bit"); + endaction + await(tx_is(40'hFFFFFFFFFF)); + repeat (1000) noAction; + dynamicAssert(dut.phy.stop_sending(), "receiver did not cork sender"); + + // Read the final two bytes from the receiver. + action + let got <- dut.receive.get(); + read_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): UART.rx = %0d, want %0d", cycles.all, cycles, got, 14); + dynamicAssert(got == 14, "wrong byte received"); + dynamicAssert(cycles < 10500, "byte not received during stop bit"); + endaction + repeat (1000) noAction; + dynamicAssert(!dut.phy.stop_sending(), "receiver did not uncork sender"); + + action + let got <- dut.receive.get(); + read_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): UART.rx = %0d, want %0d", cycles.all, cycles, got, 25); + dynamicAssert(got == 25, "wrong byte received"); + dynamicAssert(cycles < 11500, "byte not received during stop bit"); + endaction + repeat (1000) noAction; + dynamicAssert(!dut.phy.stop_sending(), "receiver did not uncork sender"); + running <= False; + endseq); + method start = fsm.start; + method done = fsm.done; +endmodule + +module mkTestTransmitter(Test); + let testflags <- mkTestFlags(); + let cycles <- mkCycleCounter(); + + let receiver <- mkUARTReceiver(25_000_000, 115_200); + let dut <- mkUARTTransmitter(25_000_000, 115_200); + mkConnection(toGet(dut.phy.tx_out), toPut(receiver.phy.rx_in)); + mkConnection(toGet(True), toPut(dut.phy.can_send)); + + Probe#(bit) tx_probe <- mkProbe(); + Probe#(Bit#(8)) recv_probe <- mkProbe(); + rule record_tx; + tx_probe <= dut.phy.tx_out; + endrule + + let fsm <- mkFSM(seq + // Let UART initialize and settle + repeat (61) noAction; + + action + dut.send.put(42); + cycles.reset(); + if (testflags.verbose) + $display("%0d: send", cycles.all); + endaction + action + let got <- receiver.receive.get(); + recv_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): received %0d", cycles.all, cycles, got); + dynamicAssert(got == 42, "wrong value received"); + dynamicAssert(cycles < 2000, "value received too late"); + endaction + + repeat(1000) noAction; + par + seq + dut.send.put(1); + dut.send.put(2); + dut.send.put(5); + dut.send.put(3); + dut.send.put(4); + endseq + + seq + action + let got <- receiver.receive.get(); + recv_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): received %0d", cycles.all, cycles, got); + dynamicAssert(got == 1, "wrong byte received"); + endaction + action + let got <- receiver.receive.get(); + recv_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): received %0d", cycles.all, cycles, got); + dynamicAssert(got == 2, "wrong byte received"); + endaction + action + let got <- receiver.receive.get(); + recv_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): received %0d", cycles.all, cycles, got); + dynamicAssert(got == 5, "wrong byte received"); + endaction + action + let got <- receiver.receive.get(); + recv_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): received %0d", cycles.all, cycles, got); + dynamicAssert(got == 3, "wrong byte received"); + endaction + action + let got <- receiver.receive.get(); + recv_probe <= got; + if (testflags.verbose) + $display("%0d (%0d): received %0d", cycles.all, cycles, got); + dynamicAssert(got == 4, "wrong byte received"); + endaction + endseq + endpar + endseq); + + method start = fsm.start; + method done = fsm.done; +endmodule + +module mkTB(); + let rx_test <- mkTestReceiver(); + let tx_test <- mkTestTransmitter(); + + runTest(30000, + mkTest("UART", seq + rx_test.start(); + await(rx_test.done); + + tx_test.start(); + await(tx_test.done); + endseq)); +endmodule + +endpackage