debugger/UART: implement a UART with RTS/CTS flow control

In practice the flow control is unusable on ULX3S dev boards because
the CTS line isn't hooked up (it's instead wired to JTAG_TDO, to enable
the USB<>UART chip to serve a dual purpose as a bitbanged JTAG programmer)

Still, support for flow control is nice, for the future. And the UART
itself also works regardless of flow control, which is of course nice.
This commit is contained in:
David Anderson 2024-09-09 12:47:19 -07:00
parent cea5fde170
commit 379ebf0411
2 changed files with 508 additions and 0 deletions

243
debugger/UART.bsv Normal file
View File

@ -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

265
debugger/UART_Test.bsv Normal file
View File

@ -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