1 # SPDX-License-Identifier: LGPLv3+
2 # Copyright (C) 2022 Cesar Strauss <cestrauss@gmail.com>
3 # Sponsored by NLnet and NGI POINTER under EU Grants 871528 and 957073
4 # Part of the Libre-SOC Project.
7 Wrapper around a single port (1R or 1W) SRAM, to make a multi-port regfile.
9 This SRAM primitive has one cycle delay for reads, and, after a write,
10 it reads the value just written. The goal is to use it to make at least an
13 See https://bugs.libre-soc.org/show_bug.cgi?id=781 and
14 https://bugs.libre-soc.org/show_bug.cgi?id=502
19 from nmigen
import Elaboratable
, Module
, Memory
, Signal
20 from nmigen
.back
import rtlil
22 from nmutil
.formaltest
import FHDLTestCase
25 class SinglePortSRAM(Elaboratable
):
27 Model of a single port SRAM, which can be simulated, verified and/or
28 synthesized to an FPGA.
30 :param addr_width: width of the address bus
31 :param data_width: width of the data bus
32 :param we_width: number of write enable lines
34 def __init__(self
, addr_width
, data_width
, we_width
):
35 self
.addr_width
= addr_width
36 self
.data_width
= data_width
37 self
.we_width
= we_width
38 self
.d
= Signal(data_width
)
40 self
.q
= Signal(data_width
)
42 self
.a
= Signal(addr_width
)
43 """ read/write address"""
44 self
.we
= Signal(we_width
)
47 def elaborate(self
, _
):
50 depth
= 1 << self
.addr_width
51 granularity
= self
.data_width
// self
.we_width
52 mem
= Memory(width
=self
.data_width
, depth
=depth
)
53 # create read and write ports
54 # By connecting the same address to both ports, they behave, in fact,
55 # as a single, "half-duplex" port.
56 # The transparent attribute means that, on a write, we read the new
57 # value, on the next cycle
58 # Note that nmigen memories have a one cycle delay, for reads,
60 m
.submodules
.rdport
= rdport
= mem
.read_port(transparent
=True)
61 m
.submodules
.wrport
= wrport
= mem
.write_port(granularity
=granularity
)
62 # duplicate the address to both ports
63 m
.d
.comb
+= wrport
.addr
.eq(self
.a
)
64 m
.d
.comb
+= rdport
.addr
.eq(self
.a
)
66 m
.d
.comb
+= wrport
.en
.eq(self
.we
)
68 m
.d
.comb
+= wrport
.data
.eq(self
.d
)
69 m
.d
.comb
+= self
.q
.eq(rdport
.data
)
81 def create_ilang(dut
, ports
, test_name
):
82 vl
= rtlil
.convert(dut
, name
=test_name
, ports
=ports
)
83 with
open("%s.il" % test_name
, "w") as f
:
87 class SinglePortSRAMTestCase(FHDLTestCase
):
89 def test_simple_rtlil():
91 Generate a simple SRAM. Try ``read_rtlil mem_simple.il; proc; show``
92 from a yosys prompt, to see the memory primitives, and
93 ``read_rtlil mem_simple.il; synth; show`` to see it implemented as
96 dut
= SinglePortSRAM(2, 4, 2)
97 create_ilang(dut
, dut
.ports(), "mem_simple")
100 def test_blkram_rtlil():
102 Generates a bigger SRAM.
103 Try ``read_rtlil mem_blkram.il; synth_ecp5; show`` from a yosys
104 prompt, to see it implemented as block RAM
106 dut
= SinglePortSRAM(10, 16, 2)
107 create_ilang(dut
, dut
.ports(), "mem_blkram")
110 if __name__
== "__main__":