Compare commits
No commits in common. "b0126a7d161c3fe487e3729621f63d07d12d3732" and "07de394ddb3438637d4038114d34a2048b448017" have entirely different histories.
b0126a7d16
...
07de394ddb
|
@ -1,243 +0,0 @@
|
||||||
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
|
|
|
@ -1,265 +0,0 @@
|
||||||
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
|
|
|
@ -1,33 +0,0 @@
|
||||||
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
|
|
|
@ -1,33 +0,0 @@
|
||||||
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
|
||||||
nextpnrWithGui
|
nextpnr
|
||||||
openfpgaloader
|
openfpgaloader
|
||||||
picocom
|
picocom
|
||||||
(python3.withPackages (py-pkgs: [
|
(python3.withPackages (py-pkgs: [
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
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
|
|
|
@ -1,76 +0,0 @@
|
||||||
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,5 +1,11 @@
|
||||||
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
|
||||||
|
@ -18,8 +24,7 @@ package PinSync;
|
||||||
// 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.
|
||||||
(* always_enabled="_write",always_ready="_read" *)
|
module mkPinSync(val init_value, PinSync#(val) ifc)
|
||||||
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();
|
||||||
|
|
||||||
Reg#(UInt#(2)) dut <- mkPinSync(0);
|
PinSync#(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,7 +2,6 @@ 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.
|
||||||
|
@ -10,134 +9,49 @@ import DReg::*;
|
||||||
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 the reset.
|
// cycle following 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));
|
||||||
|
|
||||||
// This is how many main clock ticks there are per strobe at the
|
// Because we're using integer counters to divide frequencies,
|
||||||
// target frequency. For all but the simplest cases, this will
|
// unless the clock and target frequencies divide cleanly we'll end
|
||||||
// include fractional clock cycles due to the two frequencies not
|
// up with a small amount of error.
|
||||||
// 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).
|
|
||||||
//
|
//
|
||||||
// We can improve on this by counting in larger increments modulo
|
// Strobes like this tend to be used for relatively short
|
||||||
// some maximum, and strobing on every overflow. This will make the
|
// operations before some other synchronization event happens
|
||||||
// strobe period vary slightly by the main clock's reckoning,
|
// (e.g. sending one byte on UART), so we can allow a small amount
|
||||||
// jittering around the "true" strobe time as error accumulates and
|
// of frequency error. For now, the target frequency error is fixed
|
||||||
// is then subtracted back out.
|
// at <=0.1%.
|
||||||
Real target_error = 1/1000; // 0.1%
|
Real actual_frequency = fromInteger(clock_frequency)/fromInteger(strobe_every);
|
||||||
|
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));
|
||||||
|
|
||||||
Integer incr;
|
Reg#(UInt#(32)) cnt[2] <- mkCReg(2, 0);
|
||||||
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
|
|
||||||
|
|
||||||
// Exited the loop, so either we found a good counter width to hit
|
(* no_implicit_conditions, fire_when_enabled *)
|
||||||
// the requested error, or we maxed out on a 64-bit counter.
|
rule increment;
|
||||||
if (actual_error > target_error)
|
if (cnt[0] == fromInteger(strobe_every-1))
|
||||||
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));
|
cnt[0] <= 0;
|
||||||
|
else
|
||||||
|
cnt[0] <= cnt[0]+1;
|
||||||
|
endrule
|
||||||
|
|
||||||
// The loop over-incremented by 1 after finding an acceptable error amount.
|
method Bool _read();
|
||||||
incr = incr-1;
|
return cnt[0] == 0;
|
||||||
|
endmethod
|
||||||
|
|
||||||
// Debug messages, uncomment if you're wondering what the logic
|
method Action reset();
|
||||||
// above decided for your main clock and target strobe rate.
|
cnt[1] <= 0;
|
||||||
Bool show_debug = True;
|
endmethod
|
||||||
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,12 +11,10 @@ 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
|
||||||
// 16x115_200bps strobe for a serial port. That translates to a
|
// 115_200bps strobe for a serial port. That translates to a strobe
|
||||||
// strobe that happens every 13 or 14 cycles, depending on the
|
// every 217 cycles.
|
||||||
// amount of accumulated error from the imprecise frequency
|
let dut <- mkStrobe(25_000_000, 115_200);
|
||||||
// division.
|
let want_pulse_every = 217;
|
||||||
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
|
||||||
|
@ -26,39 +24,29 @@ module mkTB();
|
||||||
endaction;
|
endaction;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
function Stmt check_one_cycle(Integer cycle_len);
|
function Stmt check_one_cycle();
|
||||||
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 < fromInteger(cycle_len))
|
while (cycles < want_pulse_every)
|
||||||
check_dut(False);
|
check_dut(False);
|
||||||
endseq;
|
endseq;
|
||||||
endfunction
|
endfunction
|
||||||
|
|
||||||
runTest(500,
|
runTest(2000,
|
||||||
mkTest("Strobe", seq
|
mkTest("Strobe", seq
|
||||||
dut.reset();
|
dut.reset();
|
||||||
check_one_cycle(14);
|
repeat(3) check_one_cycle();
|
||||||
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;
|
||||||
dut.reset();
|
par
|
||||||
check_one_cycle(14);
|
check_dut(False);
|
||||||
check_one_cycle(14);
|
dut.reset();
|
||||||
|
endpar
|
||||||
|
repeat(3) check_one_cycle();
|
||||||
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, gui=False):
|
def synth(c, target):
|
||||||
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,16 +192,11 @@ def synth(c, target, gui=False):
|
||||||
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 --freq 100"
|
pin_map_arg = f"--lpf-allow-unconstrained --lpf=lib/ulx3s_v20.lpf"
|
||||||
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")
|
||||||
if gui:
|
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')
|
||||||
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