1 # // Copyright 2018 ETH Zurich and University of Bologna.
2 # // Copyright and related rights are licensed under the Solderpad Hardware
3 # // License, Version 0.51 (the "License"); you may not use this file except in
4 # // compliance with the License. You may obtain a copy of the License at
5 # // http://solderpad.org/licenses/SHL-0.51. Unless required by applicable law
6 # // or agreed to in writing, software, hardware and materials distributed under
7 # // this License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
8 # // CONDITIONS OF ANY KIND, either express or implied. See the License for the
9 # // specific language governing permissions and limitations under the License.
14 # * This code implements a parameterizable two-port memory. Port 0 can read and
15 # * write while Port 1 can read only. The Xilinx tools will infer a BRAM with
16 # * Port 0 in "no change" mode, i.e., during a write, it retains the last read
17 # * value on the output. Port 1 (read-only) is in "write first" mode. Still, it
18 # * outputs the old data during the write cycle. Note: Port 1 outputs invalid
19 # * data in the cycle after the write when reading the same address.
21 # * For more information, see Xilinx PG058 Block Memory Generator Product Guide.
24 from nmigen
import Signal
, Module
, Const
, Cat
, Elaboratable
25 from nmigen
import Memory
30 # module ram_tp_no_change
38 # input [ADDR_WIDTH-1:0] addr0,
39 # input [ADDR_WIDTH-1:0] addr1,
40 # input [DATA_WIDTH-1:0] d_i,
41 # output [DATA_WIDTH-1:0] d0_o,
42 # output [DATA_WIDTH-1:0] d1_o
46 class ram_tp_no_change(Elaboratable
):
49 self
.we
= Signal() # input
50 self
.addr0
= Signal(ADDR_WIDTH
) # input
51 self
.addr1
= Signal(ADDR_WIDTH
) # input
52 self
.d_i
= Signal(DATA_WIDTH
) # input
53 self
.d0_o
= Signal(DATA_WIDTH
) # output
54 self
.d1_o
= Signal(DATA_WIDTH
) # output
56 DEPTH
= int(math
.pow(2, ADDR_WIDTH
))
57 self
.ram
= Memory(width
=DATA_WIDTH
, depth
=DEPTH
)
59 # localparam DEPTH = 2**ADDR_WIDTH;
61 # (* ram_style = "block" *) reg [DATA_WIDTH-1:0] ram[DEPTH];
62 # reg [DATA_WIDTH-1:0] d0;
63 # reg [DATA_WIDTH-1:0] d1;
65 # always_ff @(posedge clk) begin
66 # if(we == 1'b1) begin
69 # only change data if we==false
79 def elaborate(self
, platform
=None):
81 m
.submodules
.read_ram0
= read_ram0
= self
.ram
.read_port()
82 m
.submodules
.read_ram1
= read_ram1
= self
.ram
.read_port()
83 m
.submodules
.write_ram
= write_ram
= self
.ram
.write_port()
86 m
.d
.comb
+= write_ram
.en
.eq(self
.we
)
87 m
.d
.comb
+= write_ram
.addr
.eq(self
.addr0
)
88 m
.d
.comb
+= write_ram
.data
.eq(self
.d_i
)
91 m
.d
.comb
+= read_ram0
.addr
.eq(self
.addr0
)
92 m
.d
.comb
+= read_ram1
.addr
.eq(self
.addr1
)
93 with m
.If(self
.we
== 0):
94 m
.d
.sync
+= self
.d0_o
.eq(read_ram0
.data
)
95 m
.d
.sync
+= self
.d1_o
.eq(read_ram1
.data
)