bdcd55508194b8283970e70d4af03406f81c4f20
[soc.git] / src / iommu / axi_rab / ram_tp_no_change.py
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.
10 #
11 # /*
12 # * ram_tp_no_change
13 # *
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.
20 # *
21 # * For more information, see Xilinx PG058 Block Memory Generator Product Guide.
22 # */
23
24 from nmigen import Signal, Module, Const, Cat, Elaboratable
25 from nmigen import Memory
26
27 import math
28
29 #
30 # module ram_tp_no_change
31 # #(
32 ADDR_WIDTH = 10
33 DATA_WIDTH = 36
34 # )
35 # (
36 # input clk,
37 # input we,
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
43 # );
44
45
46 class ram_tp_no_change(Elaboratable):
47
48 def __init__(self):
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
55
56 DEPTH = int(math.pow(2, ADDR_WIDTH))
57 self.ram = Memory(DATA_WIDTH, DEPTH)
58 #
59 # localparam DEPTH = 2**ADDR_WIDTH;
60 #
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;
64 #
65 # always_ff @(posedge clk) begin
66 # if(we == 1'b1) begin
67 # ram[addr0] <= d_i;
68 # end else begin
69 # only change data if we==false
70 # d0 <= ram[addr0];
71 # end
72 # d1 <= ram[addr1];
73 # end
74 #
75 # assign d0_o = d0;
76 # assign d1_o = d1;
77 #
78
79 def elaborate(self, platform=None):
80 m = Module()
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()
84
85 # 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)
89
90 # read ports
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)
96
97 return m