Compare commits

..

No commits in common. "b0126a7d161c3fe487e3729621f63d07d12d3732" and "07de394ddb3438637d4038114d34a2048b448017" have entirely different histories.

12 changed files with 53 additions and 831 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -24,7 +24,7 @@
gotools
gtkwave
imagemagick
nextpnrWithGui
nextpnr
openfpgaloader
picocom
(python3.withPackages (py-pkgs: [

View File

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

View File

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

View File

@ -1,5 +1,11 @@
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.
//
// 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
// our design and is driving the input of mkPinSync, so we just need
// the metastability mitigation within our own domain.
(* always_enabled="_write",always_ready="_read" *)
module mkPinSync(val init_value, Reg#(val) ifc)
module mkPinSync(val init_value, PinSync#(val) ifc)
provisos(Bits#(val, _));
Reg#(val) r1 <- mkReg(init_value);

View File

@ -10,7 +10,7 @@ module mkTB();
let testflags <- mkTestFlags();
let cycles <- mkCycleCounter();
Reg#(UInt#(2)) dut <- mkPinSync(0);
PinSync#(UInt#(2)) dut <- mkPinSync(0);
function Action check_dut_val(UInt#(2) want_val);
return action

View File

@ -2,7 +2,6 @@ package Strobe;
import Real::*;
import Printf::*;
import DReg::*;
// A Strobe provides a synchronization signal to other modules, when
// an event happens at a cadence other than the module clock.
@ -10,134 +9,49 @@ import DReg::*;
interface Strobe;
method Bool _read();
// reset resets the strobe cycle, starting with a strobe on the
// cycle following the reset.
// cycle following reset.
method Action reset();
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
// target_frequency, assuming mkStrobe is being clocked at the given
// higher clock_frequency.
module mkStrobe(Integer clock_frequency, Integer target_frequency, Strobe ifc);
if (target_frequency > 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
// target frequency. For all but the simplest cases, this will
// 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).
// Because we're using integer counters to divide frequencies,
// unless the clock and target frequencies divide cleanly we'll end
// up with a small amount of error.
//
// We can improve on this by counting in larger increments modulo
// some maximum, and strobing on every overflow. This will make the
// strobe period vary slightly by the main clock's reckoning,
// jittering around the "true" strobe time as error accumulates and
// is then subtracted back out.
Real target_error = 1/1000; // 0.1%
// Strobes like this tend to be used for relatively short
// operations before some other synchronization event happens
// (e.g. sending one byte on UART), so we can allow a small amount
// of frequency error. For now, the target frequency error is fixed
// at <=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;
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
Reg#(UInt#(32)) cnt[2] <- mkCReg(2, 0);
// Exited the loop, so either we found a good counter width to hit
// the requested error, or we maxed out on a 64-bit counter.
if (actual_error > target_error)
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));
(* no_implicit_conditions, fire_when_enabled *)
rule increment;
if (cnt[0] == fromInteger(strobe_every-1))
cnt[0] <= 0;
else
cnt[0] <= cnt[0]+1;
endrule
// The loop over-incremented by 1 after finding an acceptable error amount.
incr = incr-1;
method Bool _read();
return cnt[0] == 0;
endmethod
// Debug messages, uncomment if you're wondering what the logic
// above decided for your main clock and target strobe rate.
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;
method Action reset();
cnt[1] <= 0;
endmethod
endmodule
endpackage

View File

@ -11,12 +11,10 @@ module mkTB();
let cycles <- mkCycleCounter();
// For this test, we assume we're clocked at 25MHz, and want a
// 16x115_200bps strobe for a serial port. That translates to a
// strobe that happens every 13 or 14 cycles, depending on the
// amount of accumulated error from the imprecise frequency
// division.
let dut <- mkStrobe(25_000_000, 115_200*16);
let want_pulse_every = 14;
// 115_200bps strobe for a serial port. That translates to a strobe
// every 217 cycles.
let dut <- mkStrobe(25_000_000, 115_200);
let want_pulse_every = 217;
function Action check_dut(Bool want);
return action
@ -26,39 +24,29 @@ module mkTB();
endaction;
endfunction
function Stmt check_one_cycle(Integer cycle_len);
function Stmt check_one_cycle();
return seq
action
$display("%0d: cycle start", cycles.all);
check_dut(True);
cycles.reset();
endaction
while (cycles < fromInteger(cycle_len))
while (cycles < want_pulse_every)
check_dut(False);
endseq;
endfunction
runTest(500,
runTest(2000,
mkTest("Strobe", seq
dut.reset();
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);
repeat(3) check_one_cycle();
// Reset should actually reset
repeat(10) noAction;
par
check_dut(False);
dut.reset();
check_one_cycle(14);
check_one_cycle(14);
endpar
repeat(3) check_one_cycle();
endseq));
endmodule

View File

@ -137,7 +137,7 @@ def build(c, target):
return verilog_files
@task
def synth(c, target, gui=False):
def synth(c, 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")
@ -192,16 +192,11 @@ def synth(c, target, gui=False):
pin_map_arg = f"--lpf {pin_map}"
else:
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"
nextpnr_out_json = out_nextpnr / f"{module_name.stem}_routed.json"
pin_map_arg = f"--lpf-allow-unconstrained --lpf=lib/ulx3s_v20.lpf"
nextpnr_out = out_nextpnr / module_name.with_suffix(".pnr")
nextpnr_log = out_nextpnr / module_name.with_suffix(".log")
nextpnr_timing = out_nextpnr / module_name.with_suffix(".timing.log")
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)
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')
print_filtered_paragraphs(out.stderr, "Device utilisation", "Critical path", "Max frequency", "Max delay", common_prefix="Info: ")
print(f" PNR : {nextpnr_out}")