Skip to content

Commit

Permalink
Merge branch 'master' into tilk/fifo-rs
Browse files Browse the repository at this point in the history
  • Loading branch information
tilk authored Apr 2, 2024
2 parents 8fb9b14 + 4f25673 commit 2c55017
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 7 deletions.
6 changes: 4 additions & 2 deletions coreblocks/cache/icache.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,6 @@ def elaborate(self, platform):
with m.If(refill_finish):
m.next = "LOOKUP"

accepting_requests = fsm.ongoing("LOOKUP") & ~needs_refill

# Replacement policy
way_selector = Signal(self.params.num_of_ways, reset=1)
with m.If(refill_finish):
Expand All @@ -185,6 +183,9 @@ def elaborate(self, platform):
m.d.comb += assign(mem_read_addr, prev_mem_read_addr)

mem_read_output_valid = Signal()
forwarding_response_now = Signal()
accepting_requests = ~mem_read_output_valid | forwarding_response_now

with Transaction(name="MemRead").body(
m, request=fsm.ongoing("LOOKUP") & (mem_read_output_valid | refill_error_saved)
):
Expand All @@ -194,6 +195,7 @@ def elaborate(self, platform):
tag_hit_any = reduce(operator.or_, tag_hit)

with m.If(tag_hit_any | refill_error_saved):
m.d.comb += forwarding_response_now.eq(1)
self.perf_hits.incr(m, cond=tag_hit_any)
mem_out = Signal(self.params.fetch_block_bytes * 8)
for i in OneHotSwitchDynamic(m, Cat(tag_hit)):
Expand Down
31 changes: 27 additions & 4 deletions test/cache/test_icache.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,8 @@ def setUp(self) -> None:
self.refill_requests = deque()
self.issued_requests = deque()

self.accept_refill_request = True

self.refill_in_fly = False
self.refill_word_cnt = 0
self.refill_addr = 0
Expand All @@ -347,7 +349,7 @@ def init_module(self, ways, sets) -> None:
self.cp = self.gen_params.icache_params
self.m = ICacheTestCircuit(self.gen_params)

@def_method_mock(lambda self: self.m.refiller.start_refill_mock)
@def_method_mock(lambda self: self.m.refiller.start_refill_mock, enable=lambda self: self.accept_refill_request)
def start_refill_mock(self, addr):
self.refill_requests.append(addr)
self.refill_block_cnt = 0
Expand Down Expand Up @@ -631,11 +633,21 @@ def cache_process():
# Schedule two requests and then flush
yield from self.send_req(0x00000000 + self.cp.line_size_bytes)
yield from self.send_req(0x00010000)
yield from self.m.flush_cache.call()
self.mem[0x00010000] = random.randrange(2**self.gen_params.isa.ilen)

# And accept the results
yield from self.m.flush_cache.call_init()
yield
# We cannot flush until there are two pending requests
self.assertFalse((yield from self.m.flush_cache.done()))
yield
yield from self.m.flush_cache.disable()
yield

# Accept the first response
self.assert_resp((yield from self.m.accept_res.call()))

yield from self.m.flush_cache.call()

# And accept the second response ensuring that we got old data
self.assert_resp((yield from self.m.accept_res.call()))
self.expect_refill(0x00000000 + self.cp.line_size_bytes)

Expand Down Expand Up @@ -753,6 +765,16 @@ def test_random(self):
if random.random() < 0.05:
self.add_bad_addr(i)

def refiller_ctrl():
yield Passive()

while True:
yield from self.random_wait_geom(0.4)
self.accept_refill_request = False

yield from self.random_wait_geom(0.7)
self.accept_refill_request = True

def sender():
for _ in range(iterations):
yield from self.send_req(random.randrange(0, max_addr, 4))
Expand All @@ -773,3 +795,4 @@ def receiver():
with self.run_simulation(self.m) as sim:
sim.add_sync_process(sender)
sim.add_sync_process(receiver)
sim.add_sync_process(refiller_ctrl)
11 changes: 10 additions & 1 deletion test/transactions/test_transaction_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def elaborate(self, platform):
return self.connect


FIFO_Like: TypeAlias = FIFO | Forwarder | Connect | RevConnect
FIFO_Like: TypeAlias = FIFO | Forwarder | Connect | RevConnect | Pipe


class TestFifoBase(TestCaseWithSimulator):
Expand Down Expand Up @@ -128,6 +128,15 @@ def process():
sim.add_sync_process(process)


class TestPipe(TestFifoBase):
@parameterized.expand([(0, 0), (2, 0), (0, 2), (1, 1)])
def test_fifo(self, writer_rand, reader_rand):
self.do_test_fifo(Pipe, writer_rand=writer_rand, reader_rand=reader_rand)

def test_pipelining(self):
self.do_test_fifo(Pipe, writer_rand=0, reader_rand=0)


class TestMemoryBank(TestCaseWithSimulator):
test_conf = [(9, 3, 3, 3, 14), (16, 1, 1, 3, 15), (16, 1, 1, 1, 16), (12, 3, 1, 1, 17)]

Expand Down
65 changes: 65 additions & 0 deletions transactron/lib/connectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"ConnectTrans",
"ManyToOneConnectTrans",
"StableSelectingNetwork",
"Pipe",
]


Expand Down Expand Up @@ -148,6 +149,70 @@ def _():
return m


class Pipe(Elaboratable):
"""
This module implements a `Pipe`. It is a halfway between
`Forwarder` and `2-FIFO`. In the `Pipe` data is always
stored localy, so the critical path of the data is cut, but there is a
combinational path between the control signals of the `read` and
the `write` methods. For comparison:
- in `Forwarder` there is both a data and a control combinational path
- in `2-FIFO` there are no combinational paths
The `read` method is scheduled before the `write`.
Attributes
----------
read: Method
Reads from the pipe. Accepts an empty argument, returns a structure.
Ready only if the pipe is not empty.
write: Method
Writes to the pipe. Accepts a structure, returns empty result.
Ready only if the pipe is not full.
clean: Method
Cleans the pipe. Has priority over `read` and `write` methods.
"""

def __init__(self, layout: MethodLayout):
"""
Parameters
----------
layout: record layout
The format of records forwarded.
"""
self.read = Method(o=layout)
self.write = Method(i=layout)
self.clean = Method()
self.head = Signal.like(self.read.data_out)

self.clean.add_conflict(self.read, Priority.LEFT)
self.clean.add_conflict(self.write, Priority.LEFT)

def elaborate(self, platform):
m = TModule()

reg = Signal.like(self.read.data_out)
reg_valid = Signal()

self.read.schedule_before(self.write) # to avoid combinational loops

@def_method(m, self.read, ready=reg_valid)
def _():
m.d.sync += reg_valid.eq(0)
return reg

@def_method(m, self.write, ready=~reg_valid | self.read.run)
def _(arg):
m.d.sync += reg.eq(arg)
m.d.sync += reg_valid.eq(1)

@def_method(m, self.clean)
def _():
m.d.sync += reg_valid.eq(0)

return m


class Connect(Elaboratable):
"""Forwarding by transaction simultaneity
Expand Down

0 comments on commit 2c55017

Please sign in to comment.