mirror of
https://github.com/YosysHQ/sby.git
synced 2025-04-06 14:24:08 +00:00
Tidying up of newstart.rst and fifo.sv to include as much code as possible by reference. Should reduce repetition and make it easier if changes occur in source.
183 lines
5.8 KiB
Systemverilog
183 lines
5.8 KiB
Systemverilog
// address generator/counter
|
|
module addr_gen
|
|
#( parameter MAX_DATA=16
|
|
) ( input en, clk, rst_n,
|
|
output reg [3:0] addr
|
|
);
|
|
initial addr <= 0;
|
|
|
|
// async reset
|
|
// increment address when enabled
|
|
always @(posedge clk or negedge rst_n)
|
|
if (~rst_n)
|
|
addr <= 0;
|
|
else if (en)
|
|
if (addr == MAX_DATA-1)
|
|
addr <= 0;
|
|
else
|
|
addr <= addr + 1;
|
|
endmodule
|
|
|
|
// Define our top level fifo entity
|
|
module fifo
|
|
#( parameter MAX_DATA=16
|
|
) ( input wen, ren, clk, rst_n,
|
|
input [7:0] wdata,
|
|
output [7:0] rdata,
|
|
output [4:0] count,
|
|
output full, empty
|
|
);
|
|
// fifo storage
|
|
// async read, sync write
|
|
wire [3:0] waddr, raddr;
|
|
reg [7:0] data [MAX_DATA-1:0];
|
|
always @(posedge clk)
|
|
if (wen)
|
|
data[waddr] <= wdata;
|
|
assign rdata = data[raddr];
|
|
// end storage
|
|
|
|
// addr_gen for both write and read addresses
|
|
addr_gen #(.MAX_DATA(MAX_DATA))
|
|
fifo_writer (
|
|
.en (wen || wskip),
|
|
.clk (clk ),
|
|
.rst_n (rst_n),
|
|
.addr (waddr)
|
|
);
|
|
|
|
addr_gen #(.MAX_DATA(MAX_DATA))
|
|
fifo_reader (
|
|
.en (ren || rskip),
|
|
.clk (clk ),
|
|
.rst_n (rst_n),
|
|
.addr (raddr)
|
|
);
|
|
|
|
// status signals
|
|
reg [4:0] data_count;
|
|
initial data_count <= 0;
|
|
|
|
always @(posedge clk or negedge rst_n) begin
|
|
if (~rst_n)
|
|
data_count <= 0;
|
|
else if (wen && !ren && data_count < MAX_DATA)
|
|
data_count <= data_count + 1;
|
|
else if (ren && !wen && data_count > 0)
|
|
data_count <= data_count - 1;
|
|
end
|
|
|
|
assign full = data_count == MAX_DATA;
|
|
assign empty = (data_count == 0) && rst_n;
|
|
assign count = data_count;
|
|
|
|
// overflow protection
|
|
wire wskip, rskip;
|
|
`ifndef NO_FULL_SKIP
|
|
// write while full => overwrite oldest data, move read pointer
|
|
assign rskip = wen && !ren && data_count >= MAX_DATA;
|
|
// read while empty => read invalid data, keep write pointer in sync
|
|
assign wskip = ren && !wen && data_count == 0;
|
|
`else
|
|
assign rskip = 0;
|
|
assign wskip = 0;
|
|
`endif // NO_FULL_SKIP
|
|
|
|
`ifdef FORMAL
|
|
// observers
|
|
wire [4:0] addr_diff;
|
|
assign addr_diff = waddr >= raddr
|
|
? waddr - raddr
|
|
: waddr + MAX_DATA - raddr;
|
|
|
|
reg init = 0;
|
|
always @(posedge clk) begin
|
|
if (rst_n)
|
|
init <= 1;
|
|
// if init is low we don't care about the value of rst_n
|
|
// if init is high (rst_n has ben high), then rst_n must remain high
|
|
assume (!init || init && rst_n);
|
|
end
|
|
|
|
// tests
|
|
always @(posedge clk) begin
|
|
if (rst_n) begin
|
|
// waddr and raddr can only be non zero if reset is high
|
|
w_nreset: cover (waddr || raddr);
|
|
|
|
// count never more than max
|
|
a_oflow: assert (count <= MAX_DATA);
|
|
a_oflow2: assert (waddr < MAX_DATA);
|
|
|
|
// count should be equal to the difference between writer and reader address
|
|
a_count_diff: assert (count == addr_diff
|
|
|| count == MAX_DATA && addr_diff == 0);
|
|
|
|
// count should only be able to increase or decrease by 1
|
|
a_counts: assert (count == 0
|
|
|| count == $past(count)
|
|
|| count == $past(count) + 1
|
|
|| count == $past(count) - 1);
|
|
|
|
// read/write addresses can only increase (or stay the same)
|
|
a_raddr: assert (raddr == 0
|
|
|| raddr == $past(raddr)
|
|
|| raddr == $past(raddr + 1));
|
|
a_waddr: assert (waddr == 0
|
|
|| waddr == $past(waddr)
|
|
|| waddr == $past(waddr + 1));
|
|
|
|
// full and empty work as expected
|
|
a_full: assert (!full || full && count == MAX_DATA);
|
|
w_full: cover (wen && !ren && count == MAX_DATA-1);
|
|
a_empty: assert (!empty || empty && count == 0);
|
|
w_empty: cover (ren && !wen && count == 1);
|
|
|
|
// can we corrupt our data?
|
|
w_overfill: cover ($past(rskip) && raddr);
|
|
w_underfill: cover ($past(wskip) && waddr);
|
|
end else begin
|
|
// waddr and raddr are zero while reset is low
|
|
a_reset: assert (!waddr && !raddr);
|
|
w_reset: cover (~rst_n);
|
|
|
|
// outputs are zero while reset is low
|
|
a_zero_out: assert (!empty && !full && !count);
|
|
end
|
|
end
|
|
|
|
`ifdef VERIFIC
|
|
// if we have verific we can also do the following additional tests
|
|
always @(posedge clk) begin
|
|
if (rst_n) begin
|
|
// read/write enables enable
|
|
ap_raddr2: assert property (ren |=> $changed(raddr));
|
|
ap_waddr2: assert property (wen |=> $changed(waddr));
|
|
|
|
// read/write needs enable UNLESS full/empty
|
|
ap_raddr3: assert property (!ren && !full |=> $stable(raddr));
|
|
ap_waddr3: assert property (!wen && !empty |=> $stable(waddr));
|
|
|
|
// can we corrupt our data?
|
|
// these should already be covered by ap_{r,w}addr2
|
|
ap_overfill: assert property (wen && full |=> $changed(raddr));
|
|
ap_underfill: assert property (ren && empty |=> $changed(waddr));
|
|
|
|
// change data when writing (and only when writing) so we can line
|
|
// up reads with writes
|
|
assume property (wen |=> $changed(wdata));
|
|
assume property (!wen |=> $stable(wdata));
|
|
end
|
|
end
|
|
`else // !VERIFIC
|
|
// without verific we are more limited in describing the above assumption
|
|
always @(posedge clk) begin
|
|
assume ((wen && wdata != $past(wdata))
|
|
|| (!wen && wdata == $past(wdata)));
|
|
end
|
|
`endif // VERIFIC
|
|
|
|
`endif // FORMAL
|
|
|
|
endmodule
|