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:
parent
cea5fde170
commit
379ebf0411
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue