Skip to content

Commit ba97212

Browse files
authored
Merge pull request #99 from siliconcompiler/rice/adds_asyncfifo_almost_full
Adds async fifo almost full signal + cocotb test
2 parents 4a5164b + 262c6a0 commit ba97212

File tree

8 files changed

+491
-29
lines changed

8 files changed

+491
-29
lines changed

.github/workflows/ci.yml

+24-3
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ jobs:
2626
pip install .[dev]
2727
flake8 --statistics .
2828
29-
check_paths:
30-
name: Run CI
29+
python_ci:
30+
name: Run Python Only
3131
runs-on: ubuntu-latest
3232
steps:
3333
- name: Check out Git repository
@@ -44,7 +44,7 @@ jobs:
4444
mkdir testrun
4545
cd testrun
4646
47-
pytest $GITHUB_WORKSPACE
47+
pytest $GITHUB_WORKSPACE -m "not eda"
4848
4949
lint_verilog:
5050
name: Lint Verilog Code
@@ -75,3 +75,24 @@ jobs:
7575
if: success() || steps.check-format.conclusion == 'failure'
7676
run: |
7777
verible-verilog-lint --rules_config ./.github/workflows/config/verible.rules `cat files.txt`
78+
79+
eda_ci:
80+
name: Run EDA
81+
runs-on: ubuntu-latest
82+
container: ghcr.io/siliconcompiler/sc_runner:latest
83+
steps:
84+
- name: Check out Git repository
85+
uses: actions/checkout@v4
86+
- name: Run tests
87+
run: |
88+
python3 -m venv .venv
89+
. .venv/bin/activate
90+
91+
pip install --upgrade pip
92+
pip install .[dev]
93+
94+
# change running directory
95+
mkdir testrun
96+
cd testrun
97+
98+
pytest $GITHUB_WORKSPACE -m "eda"

lambdalib/ramlib/rtl/la_asyncfifo.v

+30-21
Original file line numberDiff line numberDiff line change
@@ -21,36 +21,39 @@
2121
****************************************************************************/
2222

2323
module la_asyncfifo #(
24-
parameter DW = 32, // Memory width
25-
parameter DEPTH = 4, // FIFO depth
26-
parameter NS = 1, // Number of power supplies
27-
parameter CTRLW = 1, // width of asic ctrl interface
28-
parameter TESTW = 1, // width of asic teset interface
29-
parameter CHAOS = 0, // generates random full logic when set
30-
parameter PROP = "DEFAULT" // Pass through variable for hard macro
24+
parameter DW = 32, // Memory width
25+
parameter DEPTH = 4, // FIFO depth
26+
parameter ALMOSTFULL = 0, // FIFO depth
27+
parameter NS = 1, // Number of power supplies
28+
parameter CTRLW = 1, // width of asic ctrl interface
29+
parameter TESTW = 1, // width of asic teset interface
30+
parameter CHAOS = 0, // generates random full logic when set
31+
parameter PROP = "DEFAULT" // Pass through variable for hard macro
3132
) ( // write port
3233
input wr_clk,
3334
input wr_nreset,
34-
input [ DW-1:0] wr_din, // data to write
35-
input wr_en, // write fifo
36-
input wr_chaosmode, // randomly assert fifo full when set
37-
output reg wr_full, // fifo full
35+
input [ DW-1:0] wr_din, // data to write
36+
input wr_en, // write fifo
37+
input wr_chaosmode, // randomly assert fifo full when set
38+
output reg wr_full, // fifo full
39+
output reg wr_almost_full, // fifo almost full
3840
// read port
3941
input rd_clk,
4042
input rd_nreset,
41-
output [ DW-1:0] rd_dout, // output data (next cycle)
42-
input rd_en, // read fifo
43-
output reg rd_empty, // fifo is empty
43+
output [ DW-1:0] rd_dout, // output data (next cycle)
44+
input rd_en, // read fifo
45+
output reg rd_empty, // fifo is empty
4446
// Power signals
45-
input vss, // ground signal
46-
input [ NS-1:0] vdd, // supplies
47+
input vss, // ground signal
48+
input [ NS-1:0] vdd, // supplies
4749
// Generic interfaces
48-
input [CTRLW-1:0] ctrl, // pass through ASIC control interface
49-
input [TESTW-1:0] test // pass through ASIC test interface
50+
input [CTRLW-1:0] ctrl, // pass through ASIC control interface
51+
input [TESTW-1:0] test // pass through ASIC test interface
5052
);
5153

5254
// local params
5355
localparam AW = (DEPTH == 1) ? 1 : $clog2(DEPTH);
56+
localparam AFULLFINAL = (ALMOSTFULL != 0) ? ALMOSTFULL : DEPTH - 1;
5457

5558
// local wires
5659
reg [AW:0] wr_grayptr;
@@ -82,7 +85,7 @@ module la_asyncfifo #(
8285
wr_binptr[AW:0] <= 'b0;
8386
wr_grayptr[AW:0] <= 'b0;
8487
end else begin
85-
wr_binptr_mem[AW:0] <= (wr_binptr_mem_nxt[AW:0] == DEPTH) ? 'b0 : wr_binptr_mem_nxt[AW:0];
88+
wr_binptr_mem[AW:0] <= (wr_binptr_mem_nxt[AW:0] == DEPTH[AW:0]) ? 'b0 : wr_binptr_mem_nxt[AW:0];
8689
wr_binptr[AW:0] <= wr_binptr_nxt[AW:0];
8790
wr_grayptr[AW:0] <= wr_grayptr_nxt[AW:0];
8891
end
@@ -114,7 +117,13 @@ module la_asyncfifo #(
114117
if (~wr_nreset) wr_full <= 1'b0;
115118
else
116119
wr_full <= (wr_chaosfull & wr_chaosmode) |
117-
(fifo_used + {{AW{1'b0}}, (wr_en && ~wr_full)}) == DEPTH;
120+
(fifo_used + {{AW{1'b0}}, (wr_en && ~wr_full)}) == DEPTH[AW:0];
121+
122+
always @(posedge wr_clk or negedge wr_nreset)
123+
if (~wr_nreset) wr_almost_full <= 1'b0;
124+
else
125+
wr_almost_full <=
126+
(fifo_used + {{AW{1'b0}}, (wr_en && ~wr_full)}) > (AFULLFINAL[AW:0]-1);
118127

119128
// Write --> Read clock synchronizer
120129
for (i = 0; i < (AW + 1); i = i + 1) begin
@@ -136,7 +145,7 @@ module la_asyncfifo #(
136145
rd_binptr[AW:0] <= 'b0;
137146
rd_grayptr[AW:0] <= 'b0;
138147
end else begin
139-
rd_binptr_mem[AW:0] <= (rd_binptr_mem_nxt[AW:0] == DEPTH) ? 'b0 : rd_binptr_mem_nxt[AW:0];
148+
rd_binptr_mem[AW:0] <= (rd_binptr_mem_nxt[AW:0] == DEPTH[AW:0]) ? 'b0 : rd_binptr_mem_nxt[AW:0];
140149
rd_binptr[AW:0] <= rd_binptr_nxt[AW:0];
141150
rd_grayptr[AW:0] <= rd_grayptr_nxt[AW:0];
142151
end

lambdalib/ramlib/tests/__init__.py

Whitespace-only changes.
+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import cocotb
2+
from cocotb.triggers import RisingEdge
3+
from cocotb_bus.bus import Bus
4+
from cocotb.queue import Queue
5+
from cocotb.binary import BinaryValue
6+
7+
8+
class LaAsyncFifoWrBus(Bus):
9+
"""Cocotb bus for lambdalib async FIFO WR interface"""
10+
11+
_signals = ["wr_din", "wr_en", "wr_full"]
12+
_optional_signals = ["wr_almost_full", "wr_chaosmode"]
13+
14+
def __init__(self, entity=None, prefix=None, **kwargs):
15+
super().__init__(
16+
entity,
17+
prefix,
18+
self._signals,
19+
optional_signals=self._optional_signals,
20+
**kwargs
21+
)
22+
23+
@classmethod
24+
def from_entity(cls, entity, **kwargs):
25+
return cls(entity, **kwargs)
26+
27+
@classmethod
28+
def from_prefix(cls, entity, prefix, **kwargs):
29+
return cls(entity, prefix, **kwargs)
30+
31+
32+
class LaAsyncFifoRdBus(Bus):
33+
"""Cocotb bus for lambdalib async FIFO RD interface"""
34+
35+
_signals = ["rd_dout", "rd_en", "rd_empty"]
36+
_optional_signals = []
37+
38+
def __init__(self, entity=None, prefix=None, **kwargs):
39+
super().__init__(
40+
entity,
41+
prefix,
42+
self._signals,
43+
optional_signals=self._optional_signals,
44+
**kwargs
45+
)
46+
47+
@classmethod
48+
def from_entity(cls, entity, **kwargs):
49+
return cls(entity, **kwargs)
50+
51+
@classmethod
52+
def from_prefix(cls, entity, prefix, **kwargs):
53+
return cls(entity, prefix, **kwargs)
54+
55+
56+
class LaAsyncFifoSource:
57+
"""Driver for write side of lambdalib async fifo"""
58+
59+
def __init__(self, bus: LaAsyncFifoWrBus, clock, reset=None):
60+
self.bus = bus
61+
self.clock = clock
62+
self.reset = reset
63+
64+
self.queue = Queue()
65+
self.width = len(self.bus.wr_din)
66+
67+
self._run_cr = cocotb.start_soon(self._run())
68+
69+
self.bus.wr_chaosmode.value = 0
70+
self.bus.wr_en.value = 0
71+
72+
self.wr_en_generator = None
73+
74+
def set_wr_en_generator(self, generator):
75+
self.wr_en_generator = generator
76+
77+
async def send(self, data):
78+
await self.queue.put(data)
79+
80+
async def wait_until_idle(self):
81+
while not self.queue.empty() or self.bus.wr_en.value == 1:
82+
await RisingEdge(self.clock)
83+
84+
async def _run(self):
85+
clock_edge_event = RisingEdge(self.clock)
86+
87+
bus_val = None
88+
89+
while True:
90+
await clock_edge_event
91+
92+
fifo_full = self.bus.wr_full.value
93+
wr_en = self.bus.wr_en.value
94+
95+
if bus_val is None:
96+
bus_val = await self.queue.get()
97+
elif wr_en and not fifo_full:
98+
bus_val = None if self.queue.empty() else self.queue.get_nowait()
99+
100+
if bus_val is None:
101+
self.bus.wr_en.value = 0
102+
else:
103+
self.bus.wr_en.value = next(self.wr_en_generator) if self.wr_en_generator else 1
104+
105+
self.bus.wr_din.value = bus_val if bus_val else 0
106+
107+
108+
class LaAsyncFifoSink:
109+
"""Driver for read side of lambdalib async fifo"""
110+
111+
def __init__(self, bus: LaAsyncFifoRdBus, clock, reset=None):
112+
self.bus = bus
113+
self.clock = clock
114+
self.reset = reset
115+
116+
self.queue = Queue()
117+
self.width = len(self.bus.rd_dout)
118+
119+
self._run_cr = cocotb.start_soon(self._run())
120+
121+
self.bus.rd_en.value = 0
122+
123+
self.rd_en_generator = None
124+
self._pause = False
125+
126+
def set_rd_en_generator(self, generator):
127+
self.rd_en_generator = generator
128+
129+
def pause(self):
130+
self.bus.rd_en.value = 0
131+
self._pause = True
132+
133+
def resume(self):
134+
self._pause = False
135+
136+
async def read(self) -> BinaryValue:
137+
return await self.queue.get()
138+
139+
async def _run(self):
140+
clock_edge_event = RisingEdge(self.clock)
141+
142+
while True:
143+
await clock_edge_event
144+
145+
fifo_empty = self.bus.rd_empty.value
146+
147+
if self._pause:
148+
self.bus.rd_en.value = 0
149+
elif self.rd_en_generator:
150+
self.bus.rd_en.value = next(self.rd_en_generator)
151+
else:
152+
self.bus.rd_en.value = 1
153+
154+
if self.bus.rd_en.value and not fifo_empty:
155+
self.queue.put_nowait(self.bus.rd_dout.value)

0 commit comments

Comments
 (0)