Compare commits
7 Commits
07de394ddb
...
b0126a7d16
Author | SHA1 | Date |
---|---|---|
David Anderson | b0126a7d16 | |
David Anderson | a08fd421fe | |
David Anderson | 379ebf0411 | |
David Anderson | cea5fde170 | |
David Anderson | 1b85c3e216 | |
David Anderson | b527a62ab8 | |
David Anderson | d10a548bc1 |
|
@ -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
|
|
@ -0,0 +1,33 @@
|
||||||
|
package Top;
|
||||||
|
|
||||||
|
import GetPut::*;
|
||||||
|
|
||||||
|
import UART::*;
|
||||||
|
|
||||||
|
(* always_enabled *)
|
||||||
|
interface Top;
|
||||||
|
(* prefix="" *)
|
||||||
|
method Action rx_in((* port="rx_in" *) bit b);
|
||||||
|
method bit tx_out();
|
||||||
|
method Bit#(8) leds();
|
||||||
|
endinterface
|
||||||
|
|
||||||
|
(* synthesize *)
|
||||||
|
module mkTop(Top);
|
||||||
|
let _ret <- mkUART(100_000_000, 115_200);
|
||||||
|
disableFlowControl(_ret);
|
||||||
|
|
||||||
|
Reg#(Bit#(8)) leds_out <- mkReg(0);
|
||||||
|
|
||||||
|
rule recv;
|
||||||
|
let b <- _ret.receive.get();
|
||||||
|
_ret.send.put(b);
|
||||||
|
leds_out <= b;
|
||||||
|
endrule
|
||||||
|
|
||||||
|
method rx_in = _ret.phy.rx_in;
|
||||||
|
method tx_out = _ret.phy.tx_out;
|
||||||
|
method leds = leds_out._read;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,33 @@
|
||||||
|
BLOCK RESETPATHS;
|
||||||
|
BLOCK ASYNCPATHS;
|
||||||
|
|
||||||
|
LOCATE COMP "CLK" SITE "G2";
|
||||||
|
IOBUF PORT "CLK" PULLMODE=NONE IO_TYPE=LVCMOS33;
|
||||||
|
FREQUENCY PORT "CLK" 150 MHZ;
|
||||||
|
|
||||||
|
SYSCONFIG CONFIG_IOVOLTAGE=3.3 COMPRESS_CONFIG=ON MCCLK_FREQ=62 SLAVE_SPI_PORT=DISABLE MASTER_SPI_PORT=ENABLE SLAVE_PARALLEL_PORT=DISABLE;
|
||||||
|
|
||||||
|
LOCATE COMP "tx_out" SITE "L4"; # FPGA transmits to ftdi
|
||||||
|
LOCATE COMP "rx_in" SITE "M1"; # FPGA receives from ftdi
|
||||||
|
IOBUF PORT "tx_out" PULLMODE=UP IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "rx_in" PULLMODE=UP IO_TYPE=LVCMOS33;
|
||||||
|
|
||||||
|
LOCATE COMP "leds[7]" SITE "H3";
|
||||||
|
LOCATE COMP "leds[6]" SITE "E1";
|
||||||
|
LOCATE COMP "leds[5]" SITE "E2";
|
||||||
|
LOCATE COMP "leds[4]" SITE "D1";
|
||||||
|
LOCATE COMP "leds[3]" SITE "D2";
|
||||||
|
LOCATE COMP "leds[2]" SITE "C1";
|
||||||
|
LOCATE COMP "leds[1]" SITE "C2";
|
||||||
|
LOCATE COMP "leds[0]" SITE "B2";
|
||||||
|
IOBUF PORT "leds[0]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "leds[1]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "leds[2]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "leds[3]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "leds[4]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "leds[5]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "leds[6]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
IOBUF PORT "leds[7]" PULLMODE=NONE IO_TYPE=LVCMOS33 DRIVE=4;
|
||||||
|
|
||||||
|
LOCATE COMP "RST_N" SITE "B3";
|
||||||
|
IOBUF PORT "RST_N" PULLMODE=DOWN IO_TYPE=LVCMOS33;
|
|
@ -24,7 +24,7 @@
|
||||||
gotools
|
gotools
|
||||||
gtkwave
|
gtkwave
|
||||||
imagemagick
|
imagemagick
|
||||||
nextpnr
|
nextpnrWithGui
|
||||||
openfpgaloader
|
openfpgaloader
|
||||||
picocom
|
picocom
|
||||||
(python3.withPackages (py-pkgs: [
|
(python3.withPackages (py-pkgs: [
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package GlitchFilter;
|
||||||
|
|
||||||
|
import Cntrs::*;
|
||||||
|
|
||||||
|
(* always_enabled="_write",always_ready="_read" *)
|
||||||
|
module mkGlitchFilter(Integer hysteresis, bit init_value, Reg#(bit) ifc);
|
||||||
|
Wire#(bit) in <- mkBypassWire();
|
||||||
|
UCount cnt <- mkUCount(init_value == 0 ? 0 : hysteresis, hysteresis);
|
||||||
|
Reg#(bit) out[2] <- mkCReg(2, init_value);
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule incr (cnt.isLessThan(fromInteger(hysteresis)) && in == 1);
|
||||||
|
cnt.incr(1);
|
||||||
|
endrule
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule decr (cnt.isGreaterThan(0) && in == 0);
|
||||||
|
cnt.decr(1);
|
||||||
|
endrule
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule set_out (cnt.isEqual(0) || cnt.isEqual(fromInteger(hysteresis)));
|
||||||
|
out[0] <= cnt.isEqual(0) ? 0 : 1;
|
||||||
|
endrule
|
||||||
|
|
||||||
|
method _write = in._write;
|
||||||
|
method _read = out[1]._read;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -0,0 +1,76 @@
|
||||||
|
package GlitchFilter_Test;
|
||||||
|
|
||||||
|
import Assert::*;
|
||||||
|
import StmtFSM::*;
|
||||||
|
|
||||||
|
import GlitchFilter::*;
|
||||||
|
import Testing::*;
|
||||||
|
|
||||||
|
module mkTB();
|
||||||
|
let testflags <- mkTestFlags();
|
||||||
|
let cycles <- mkCycleCounter();
|
||||||
|
|
||||||
|
let dut <- mkGlitchFilter(3, 0);
|
||||||
|
|
||||||
|
// Slight indirection to appease the compiler: GlitchFilter
|
||||||
|
// requires a write on every cycle, and with just the test FSM, the
|
||||||
|
// compiler can't prove that this is satisfied. So, give it a
|
||||||
|
// default value when the test isn't driving the input, on the
|
||||||
|
// first few cycles of the test.
|
||||||
|
Wire#(bit) pin_in <- mkDWire(0);
|
||||||
|
(* no_implicit_conditions,fire_when_enabled *)
|
||||||
|
rule push_in;
|
||||||
|
dut <= pin_in;
|
||||||
|
endrule
|
||||||
|
|
||||||
|
function Action check_dut(bit in, bit want_out);
|
||||||
|
return action
|
||||||
|
if (testflags.verbose)
|
||||||
|
$display("%0d: GlitchFilter(%0d) => %0d, want %0d", cycles.all, in, dut, want_out);
|
||||||
|
dynamicAssert(dut == want_out, "wrong GlitchFilter output");
|
||||||
|
pin_in <= in;
|
||||||
|
endaction;
|
||||||
|
endfunction
|
||||||
|
|
||||||
|
runTest(100,
|
||||||
|
mkTest("GlitchFilter", seq
|
||||||
|
// Simple 0->1 transition
|
||||||
|
check_dut(0, 0);
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(1, 1);
|
||||||
|
check_dut(1, 1);
|
||||||
|
|
||||||
|
// Simple 1->0 transition
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(0, 0);
|
||||||
|
check_dut(0, 0);
|
||||||
|
|
||||||
|
// Glitchy 0->1
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(0, 0);
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(0, 0);
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(1, 0);
|
||||||
|
check_dut(1, 1);
|
||||||
|
check_dut(1, 1);
|
||||||
|
|
||||||
|
// Glitchy 1->0
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(1, 1);
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(1, 1);
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(0, 1);
|
||||||
|
check_dut(0, 0);
|
||||||
|
check_dut(0, 0);
|
||||||
|
endseq));
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
endpackage
|
|
@ -1,11 +1,5 @@
|
||||||
package PinSync;
|
package PinSync;
|
||||||
|
|
||||||
(* always_ready, always_enabled *)
|
|
||||||
interface PinSync#(type val);
|
|
||||||
method Action _write(val a);
|
|
||||||
method val _read();
|
|
||||||
endinterface
|
|
||||||
|
|
||||||
// mkPinSync builds a synchronizer for use with asynchronous inputs.
|
// mkPinSync builds a synchronizer for use with asynchronous inputs.
|
||||||
//
|
//
|
||||||
// You should only use this to capture asynchronous inputs coming from
|
// You should only use this to capture asynchronous inputs coming from
|
||||||
|
@ -24,7 +18,8 @@ endinterface
|
||||||
// source domain. Conceptually, we assume that register exists outside
|
// source domain. Conceptually, we assume that register exists outside
|
||||||
// our design and is driving the input of mkPinSync, so we just need
|
// our design and is driving the input of mkPinSync, so we just need
|
||||||
// the metastability mitigation within our own domain.
|
// the metastability mitigation within our own domain.
|
||||||
module mkPinSync(val init_value, PinSync#(val) ifc)
|
(* always_enabled="_write",always_ready="_read" *)
|
||||||
|
module mkPinSync(val init_value, Reg#(val) ifc)
|
||||||
provisos(Bits#(val, _));
|
provisos(Bits#(val, _));
|
||||||
|
|
||||||
Reg#(val) r1 <- mkReg(init_value);
|
Reg#(val) r1 <- mkReg(init_value);
|
||||||
|
|
|
@ -10,7 +10,7 @@ module mkTB();
|
||||||
let testflags <- mkTestFlags();
|
let testflags <- mkTestFlags();
|
||||||
let cycles <- mkCycleCounter();
|
let cycles <- mkCycleCounter();
|
||||||
|
|
||||||
PinSync#(UInt#(2)) dut <- mkPinSync(0);
|
Reg#(UInt#(2)) dut <- mkPinSync(0);
|
||||||
|
|
||||||
function Action check_dut_val(UInt#(2) want_val);
|
function Action check_dut_val(UInt#(2) want_val);
|
||||||
return action
|
return action
|
||||||
|
|
142
lib/Strobe.bsv
142
lib/Strobe.bsv
|
@ -2,6 +2,7 @@ package Strobe;
|
||||||
|
|
||||||
import Real::*;
|
import Real::*;
|
||||||
import Printf::*;
|
import Printf::*;
|
||||||
|
import DReg::*;
|
||||||
|
|
||||||
// A Strobe provides a synchronization signal to other modules, when
|
// A Strobe provides a synchronization signal to other modules, when
|
||||||
// an event happens at a cadence other than the module clock.
|
// an event happens at a cadence other than the module clock.
|
||||||
|
@ -9,49 +10,134 @@ import Printf::*;
|
||||||
interface Strobe;
|
interface Strobe;
|
||||||
method Bool _read();
|
method Bool _read();
|
||||||
// reset resets the strobe cycle, starting with a strobe on the
|
// reset resets the strobe cycle, starting with a strobe on the
|
||||||
// cycle following reset.
|
// cycle following the reset.
|
||||||
method Action reset();
|
method Action reset();
|
||||||
endinterface
|
endinterface
|
||||||
|
|
||||||
|
module mkStrobeRaw(UInt#(sz) increment, UInt#(sz) max, Strobe ifc);
|
||||||
|
Reg#(UInt#(sz)) cnt <- mkReg(0);
|
||||||
|
PulseWire want_reset <- mkPulseWire();
|
||||||
|
Reg#(Bool) strobe_out <- mkDReg(False);
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule increment;
|
||||||
|
if (want_reset) begin
|
||||||
|
cnt <= 0;
|
||||||
|
strobe_out <= True;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
UInt#(TAdd#(sz, 1)) new_cnt = extend(cnt)+extend(increment);
|
||||||
|
if (new_cnt >= extend(max)) begin
|
||||||
|
new_cnt = new_cnt - extend(max);
|
||||||
|
strobe_out <= True;
|
||||||
|
end
|
||||||
|
cnt <= truncate(new_cnt);
|
||||||
|
end
|
||||||
|
endrule
|
||||||
|
|
||||||
|
method Bool _read = strobe_out._read;
|
||||||
|
method Action reset();
|
||||||
|
want_reset.send();
|
||||||
|
endmethod
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module mkStrobeRawOld(UInt#(sz) increment, UInt#(sz) max, Strobe ifc);
|
||||||
|
Reg#(UInt#(sz)) cnt[2] <- mkCReg(2, 0);
|
||||||
|
Reg#(Bool) pulse <- mkDReg(False);
|
||||||
|
PulseWire pw <- mkPulseWireOR();
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule increment;
|
||||||
|
UInt#(TAdd#(sz, 1)) new_cnt = extend(cnt[0])+extend(increment);
|
||||||
|
if (new_cnt >= extend(max)) begin
|
||||||
|
cnt[0] <= truncate(new_cnt - extend(max));
|
||||||
|
pulse <= True;
|
||||||
|
end
|
||||||
|
else
|
||||||
|
cnt[0] <= truncate(new_cnt);
|
||||||
|
endrule
|
||||||
|
|
||||||
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
|
rule strobe (pulse);
|
||||||
|
pw.send();
|
||||||
|
endrule
|
||||||
|
|
||||||
|
method _read = pw._read;
|
||||||
|
method Action reset();
|
||||||
|
cnt[1] <= increment;
|
||||||
|
pw.send();
|
||||||
|
endmethod
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module mkStrobeRawRec(UInt#(n) hack, Integer increment, Integer max, Strobe ifc);
|
||||||
|
let register_width = ceil(log2(fromInteger(max)));
|
||||||
|
if (valueOf(n) < register_width) begin
|
||||||
|
UInt#(TAdd#(n, 1)) hack2 = 0;
|
||||||
|
let _ret <- mkStrobeRawRec(hack2, increment, max);
|
||||||
|
return _ret;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
UInt#(n) hack2 = fromInteger(increment);
|
||||||
|
let _ret <- mkStrobeRaw(hack2, fromInteger(max));
|
||||||
|
return _ret;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
// mkStrobe returns a Strobe that triggers at the given
|
// mkStrobe returns a Strobe that triggers at the given
|
||||||
// target_frequency, assuming mkStrobe is being clocked at the given
|
// target_frequency, assuming mkStrobe is being clocked at the given
|
||||||
// higher clock_frequency.
|
// higher clock_frequency.
|
||||||
module mkStrobe(Integer clock_frequency, Integer target_frequency, Strobe ifc);
|
module mkStrobe(Integer clock_frequency, Integer target_frequency, Strobe ifc);
|
||||||
if (target_frequency > clock_frequency)
|
if (target_frequency > clock_frequency)
|
||||||
error("mkStrobe target_frequency must be less than clock_frequency");
|
error("mkStrobe target_frequency must be less than clock_frequency");
|
||||||
let strobe_every = round(fromInteger(clock_frequency)/fromInteger(target_frequency));
|
|
||||||
|
|
||||||
// Because we're using integer counters to divide frequencies,
|
// This is how many main clock ticks there are per strobe at the
|
||||||
// unless the clock and target frequencies divide cleanly we'll end
|
// target frequency. For all but the simplest cases, this will
|
||||||
// up with a small amount of error.
|
// include fractional clock cycles due to the two frequencies not
|
||||||
|
// dividing evenly.
|
||||||
|
Real target_ratio = fromInteger(clock_frequency)/fromInteger(target_frequency);
|
||||||
|
|
||||||
|
// At this point we could round the target ratio and instantiate a
|
||||||
|
// counter for that amount, but the resulting strobe frequency can
|
||||||
|
// be quite drastically off target (empirically I've seen up to 5%
|
||||||
|
// without trying).
|
||||||
//
|
//
|
||||||
// Strobes like this tend to be used for relatively short
|
// We can improve on this by counting in larger increments modulo
|
||||||
// operations before some other synchronization event happens
|
// some maximum, and strobing on every overflow. This will make the
|
||||||
// (e.g. sending one byte on UART), so we can allow a small amount
|
// strobe period vary slightly by the main clock's reckoning,
|
||||||
// of frequency error. For now, the target frequency error is fixed
|
// jittering around the "true" strobe time as error accumulates and
|
||||||
// at <=0.1%.
|
// is then subtracted back out.
|
||||||
Real actual_frequency = fromInteger(clock_frequency)/fromInteger(strobe_every);
|
Real target_error = 1/1000; // 0.1%
|
||||||
Real frequency_error_pct = abs(fromInteger(target_frequency)-actual_frequency) / fromInteger(target_frequency) * 100;
|
|
||||||
if (frequency_error_pct > 0.1)
|
|
||||||
error(sprintf("mkStrobe actual frequency is %0f, %0f%% error vs. requested %0d. Your clock_frequency and target_frequency are probably too near each other.", actual_frequency, frequency_error_pct, target_frequency));
|
|
||||||
|
|
||||||
Reg#(UInt#(32)) cnt[2] <- mkCReg(2, 0);
|
Integer incr;
|
||||||
|
Integer max;
|
||||||
|
Real actual_error = 1;
|
||||||
|
for (incr=1; incr<=32 && actual_error > target_error; incr=incr+1) begin
|
||||||
|
Real mul_ratio = fromInteger(incr)*target_ratio;
|
||||||
|
max = round(mul_ratio);
|
||||||
|
actual_error = abs(mul_ratio - fromInteger(max))/fromInteger(max);
|
||||||
|
end
|
||||||
|
|
||||||
(* no_implicit_conditions, fire_when_enabled *)
|
// Exited the loop, so either we found a good counter width to hit
|
||||||
rule increment;
|
// the requested error, or we maxed out on a 64-bit counter.
|
||||||
if (cnt[0] == fromInteger(strobe_every-1))
|
if (actual_error > target_error)
|
||||||
cnt[0] <= 0;
|
error(sprintf("cannot construct a strobe running at %0dHz from a main clock of %0dHz without exceeding the target frequency error of %0d%%", target_frequency, clock_frequency, target_error*100));
|
||||||
else
|
|
||||||
cnt[0] <= cnt[0]+1;
|
|
||||||
endrule
|
|
||||||
|
|
||||||
method Bool _read();
|
// The loop over-incremented by 1 after finding an acceptable error amount.
|
||||||
return cnt[0] == 0;
|
incr = incr-1;
|
||||||
endmethod
|
|
||||||
|
|
||||||
method Action reset();
|
// Debug messages, uncomment if you're wondering what the logic
|
||||||
cnt[1] <= 0;
|
// above decided for your main clock and target strobe rate.
|
||||||
endmethod
|
Bool show_debug = True;
|
||||||
|
if (show_debug) begin
|
||||||
|
Real actual_ratio = fromInteger(max)/fromInteger(incr);
|
||||||
|
Real actual_freq_low = fromInteger(clock_frequency)/fromInteger(ceil(actual_ratio));
|
||||||
|
Real actual_freq_high = fromInteger(clock_frequency)/fromInteger(floor(actual_ratio));
|
||||||
|
Real actual_freq_avg = fromInteger(clock_frequency)/actual_ratio;
|
||||||
|
messageM(sprintf("Strobe at %0dHz given clock at %0dHz:\n count by %0d mod %0d\n strobing every %d to %d cycles\n effective strobe frequency %0.2fHz to %0.2fHz\n avg frequency %0.2fHz (%0.2f%% error)", target_frequency, clock_frequency, incr, max, floor(actual_ratio), ceil(actual_ratio), actual_freq_low, actual_freq_high, actual_freq_avg, actual_error*100));
|
||||||
|
end
|
||||||
|
|
||||||
|
let _ret <- mkStrobeRawRec(UInt#(1) ' (0), incr, max);
|
||||||
|
return _ret;
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
endpackage
|
endpackage
|
||||||
|
|
|
@ -11,10 +11,12 @@ module mkTB();
|
||||||
let cycles <- mkCycleCounter();
|
let cycles <- mkCycleCounter();
|
||||||
|
|
||||||
// For this test, we assume we're clocked at 25MHz, and want a
|
// For this test, we assume we're clocked at 25MHz, and want a
|
||||||
// 115_200bps strobe for a serial port. That translates to a strobe
|
// 16x115_200bps strobe for a serial port. That translates to a
|
||||||
// every 217 cycles.
|
// strobe that happens every 13 or 14 cycles, depending on the
|
||||||
let dut <- mkStrobe(25_000_000, 115_200);
|
// amount of accumulated error from the imprecise frequency
|
||||||
let want_pulse_every = 217;
|
// division.
|
||||||
|
let dut <- mkStrobe(25_000_000, 115_200*16);
|
||||||
|
let want_pulse_every = 14;
|
||||||
|
|
||||||
function Action check_dut(Bool want);
|
function Action check_dut(Bool want);
|
||||||
return action
|
return action
|
||||||
|
@ -24,29 +26,39 @@ module mkTB();
|
||||||
endaction;
|
endaction;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function Stmt check_one_cycle();
|
function Stmt check_one_cycle(Integer cycle_len);
|
||||||
return seq
|
return seq
|
||||||
action
|
action
|
||||||
|
$display("%0d: cycle start", cycles.all);
|
||||||
check_dut(True);
|
check_dut(True);
|
||||||
cycles.reset();
|
cycles.reset();
|
||||||
endaction
|
endaction
|
||||||
while (cycles < want_pulse_every)
|
while (cycles < fromInteger(cycle_len))
|
||||||
check_dut(False);
|
check_dut(False);
|
||||||
endseq;
|
endseq;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
runTest(2000,
|
runTest(500,
|
||||||
mkTest("Strobe", seq
|
mkTest("Strobe", seq
|
||||||
dut.reset();
|
dut.reset();
|
||||||
repeat(3) check_one_cycle();
|
check_one_cycle(14);
|
||||||
|
check_one_cycle(14);
|
||||||
|
check_one_cycle(13);
|
||||||
|
check_one_cycle(14);
|
||||||
|
check_one_cycle(13);
|
||||||
|
check_one_cycle(14);
|
||||||
|
check_one_cycle(13);
|
||||||
|
check_one_cycle(14);
|
||||||
|
check_one_cycle(14);
|
||||||
|
check_one_cycle(13);
|
||||||
|
check_one_cycle(14);
|
||||||
|
check_one_cycle(13);
|
||||||
|
|
||||||
// Reset should actually reset
|
// Reset should actually reset
|
||||||
repeat(10) noAction;
|
repeat(10) noAction;
|
||||||
par
|
|
||||||
check_dut(False);
|
|
||||||
dut.reset();
|
dut.reset();
|
||||||
endpar
|
check_one_cycle(14);
|
||||||
repeat(3) check_one_cycle();
|
check_one_cycle(14);
|
||||||
endseq));
|
endseq));
|
||||||
endmodule
|
endmodule
|
||||||
|
|
||||||
|
|
11
tasks.py
11
tasks.py
|
@ -137,7 +137,7 @@ def build(c, target):
|
||||||
return verilog_files
|
return verilog_files
|
||||||
|
|
||||||
@task
|
@task
|
||||||
def synth(c, target):
|
def synth(c, target, gui=False):
|
||||||
target = resolve_synth_target(target)
|
target = resolve_synth_target(target)
|
||||||
|
|
||||||
out_info, out_verilog, out_bsc, out_yosys, out_nextpnr = ensure_build_dirs(target, "info", "verilog", "bsc", "yosys", "nextpnr")
|
out_info, out_verilog, out_bsc, out_yosys, out_nextpnr = ensure_build_dirs(target, "info", "verilog", "bsc", "yosys", "nextpnr")
|
||||||
|
@ -192,11 +192,16 @@ def synth(c, target):
|
||||||
pin_map_arg = f"--lpf {pin_map}"
|
pin_map_arg = f"--lpf {pin_map}"
|
||||||
else:
|
else:
|
||||||
print(f"WARNING: no pin map at {pin_map}, executing place&route with no constraints")
|
print(f"WARNING: no pin map at {pin_map}, executing place&route with no constraints")
|
||||||
pin_map_arg = f"--lpf-allow-unconstrained --lpf=lib/ulx3s_v20.lpf"
|
pin_map_arg = f"--lpf-allow-unconstrained --lpf=lib/ulx3s_v20.lpf --freq 100"
|
||||||
|
nextpnr_out_json = out_nextpnr / f"{module_name.stem}_routed.json"
|
||||||
nextpnr_out = out_nextpnr / module_name.with_suffix(".pnr")
|
nextpnr_out = out_nextpnr / module_name.with_suffix(".pnr")
|
||||||
nextpnr_log = out_nextpnr / module_name.with_suffix(".log")
|
nextpnr_log = out_nextpnr / module_name.with_suffix(".log")
|
||||||
nextpnr_timing = out_nextpnr / module_name.with_suffix(".timing.log")
|
nextpnr_timing = out_nextpnr / module_name.with_suffix(".timing.log")
|
||||||
out = c.run(f"nextpnr-ecp5 --85k --detailed-timing-report -l {nextpnr_log} --report {nextpnr_timing} --json {yosys_json} --package=CABGA381 {pin_map_arg} --speed=6 --textcfg {nextpnr_out}", hide='stderr')
|
if gui:
|
||||||
|
cmd = f"nextpnr-ecp5 --85k --detailed-timing-report --report {nextpnr_timing} --json {yosys_json} --gui --gui-no-aa --package=CABGA381 {pin_map_arg} --speed=6 --textcfg {nextpnr_out}"
|
||||||
|
else:
|
||||||
|
cmd = f"nextpnr-ecp5 --85k --detailed-timing-report -l {nextpnr_log} --report {nextpnr_timing} --json {yosys_json} --write {nextpnr_out_json} --package=CABGA381 {pin_map_arg} --speed=6 --textcfg {nextpnr_out}"
|
||||||
|
out = c.run(cmd)
|
||||||
|
|
||||||
print_filtered_paragraphs(out.stderr, "Device utilisation", "Critical path", "Max frequency", "Max delay", common_prefix="Info: ")
|
print_filtered_paragraphs(out.stderr, "Device utilisation", "Critical path", "Max frequency", "Max delay", common_prefix="Info: ")
|
||||||
print(f" PNR : {nextpnr_out}")
|
print(f" PNR : {nextpnr_out}")
|
||||||
|
|
Loading…
Reference in New Issue