xref: /XiangShan/scripts/vlsi_mem_gen (revision 7ee2486883a66b728f83fb2db97c266345e9d2db)
1#! /usr/bin/env python3
2
3# See LICENSE.SiFive for license details.
4# See LICENSE.Berkeley for license details.
5
6import sys
7import math
8
9use_latches = 0
10
11class VerilogModuleGenerator(object):
12  def __init__(self, name):
13    self.name = name
14    self.port_spec = []
15    self.decl = []
16    self.combinational = []
17    self.sequential = []
18
19  def __format_width(self, width):
20    return "[{}:0] ".format(width-1) if width > 1 else ""
21
22  def __format_depth(self, depth):
23    return " [{}:0]".format(depth-1) if depth > 1 else ""
24
25  def add_io(self, io_type, width, name):
26    width_str = self.__format_width(width)
27    # print(io_type, width_str, name)
28    self.port_spec.append(f'{io_type} {width_str}{name}')
29
30  def add_input(self, width, name):
31    self.add_io("input", width, name)
32
33  def add_output(self, width, name):
34    self.add_io("output", width, name)
35
36  def add_decl(self, decl_type, width, name, depth=1):
37    width_str = self.__format_width(width)
38    depth_str = self.__format_depth(depth)
39    self.decl.append(f"{decl_type} {width_str}{name}{depth_str};")
40
41  def add_decl_reg(self, width, name, depth=1):
42    self.add_decl("reg", width, name, depth)
43
44  def add_decl_ram(self, width, name, depth=1):
45    width_str = self.__format_width(width)
46    depth_str = " [{}:0]".format(depth-1)
47    self.decl.append(f"reg {width_str}{name}{depth_str};")
48
49  def add_decl_wire(self, width, name, depth=1):
50    self.add_decl("wire", width, name, depth)
51
52  def add_decl_line(self, line):
53    self.decl.append(line)
54
55  def add_sequential(self, line):
56    self.sequential.append(line)
57
58  def add_combinational(self, line):
59    self.combinational.append(line)
60
61  def generate(self, blackbox):
62    body = "\
63  %s\n\
64  %s\n\
65  %s\n" % ('\n  '.join(self.decl), '\n  '.join(self.sequential), '\n  '.join(self.combinational))
66
67    s = "\nmodule %s(\n\
68  %s\n\
69);\n\
70\n\
71%s\
72\n\
73endmodule" % (self.name, ',\n  '.join(self.port_spec), body if not blackbox else blackbox)
74    return s
75
76
77class Reshaper(object):
78  def __init__(self, before, after):
79    # print(before, after)
80    self.conf = before
81    self.new_conf = after
82    assert(self.conf[-1] == ['write', 'read'])
83    assert(self.new_conf[-1] == ['mwrite', 'read'])
84
85  def generate(self, mem):
86    (name, width, depth, mask_gran, mask_seg, _) = self.conf
87    (new_name, new_width, new_depth, new_mask_gran, new_mask_seg, _) = self.new_conf
88    addr_bits = math.log2(depth)
89    ways = new_width // width
90    ways_bits = int(math.log2(ways))
91    mem.add_decl_wire(new_width, "data_read")
92    mem.add_decl_wire(new_width, "data_write")
93    mem.add_combinational(f"assign data_write = ")
94    sels = [f"{f'(write_way_index == {w}) ?' if w != ways-1 else ''} ({{{new_width-width}'h0, W0_data}} << {width*w})" for w in range(ways)]
95    mem.add_combinational(":\n    ".join(sels) + ";")
96    mem.add_decl_wire(ways_bits, "read_way_index")
97    mem.add_combinational(f"assign read_way_index = R0_addr[{ways_bits-1}:0];")
98    mem.add_decl_wire(ways_bits, "write_way_index")
99    mem.add_combinational(f"assign write_way_index = W0_addr[{ways_bits-1}:0];")
100    mem.add_combinational(f"{new_name} array (")
101    mem.add_combinational(f"  .W0_clk(W0_clk),")
102    mem.add_combinational(f"  .W0_addr(W0_addr[{new_width-1}:{ways_bits}]),")
103    mem.add_combinational(f"  .W0_en(W0_en),")
104    mem.add_combinational(f"  .W0_data(data_write),")
105    mem.add_combinational(f"  .W0_mask({ways}'h1 << write_way_index),")
106    mem.add_combinational(f"  .R0_clk(R0_clk),")
107    mem.add_combinational(f"  .R0_addr(R0_addr[{new_width-1}:{ways_bits}]),")
108    mem.add_combinational(f"  .R0_en(R0_en),")
109    mem.add_combinational(f"  .R0_data(data_read)")
110    mem.add_combinational(f");")
111    mem.add_combinational(f"assign R0_data = ")
112    sels = [f"{f'(read_way_index == {w}) ?' if w != ways-1 else ''} data_read[{width*(w+1)-1}:{width*w}]" for w in range(ways)]
113    mem.add_combinational(":\n    ".join(sels) + ";")
114
115
116class Spliter(object):
117  def __init__(self, before, after):
118    # print(before, after)
119    self.conf = before
120    self.new_conf = after
121    assert(self.conf[-1] == ['mrw'])
122    assert(self.new_conf[-1] == ['rw'])
123
124  def generate(self, mem):
125    (name, width, depth, mask_gran, mask_seg, _) = self.conf
126    (new_name, new_width, new_depth, new_mask_gran, new_mask_seg, _) = self.new_conf
127    assert(depth == new_depth)
128    ways = width // new_width
129    for i in range(ways):
130      data_slice = f"[{new_width*(i+1)-1}:{new_width*i}]"
131      mem.add_combinational(f"{new_name} array_{i} (")
132      mem.add_combinational(f"  .RW0_clk(RW0_clk),")
133      mem.add_combinational(f"  .RW0_addr(RW0_addr),")
134      mem.add_combinational(f"  .RW0_en(RW0_en),")
135      mem.add_combinational(f"  .RW0_wmode(RW0_wmode && RW0_wmask[{i}]),")
136      mem.add_combinational(f"  .RW0_wdata(RW0_wdata{data_slice}),")
137      mem.add_combinational(f"  .RW0_rdata(RW0_rdata{data_slice})")
138      mem.add_combinational(f");")
139
140class SRAM(object):
141  def __init__(self, line):
142    self.parse_line(line)
143    self.prepare_module()
144
145  def parse_line(self, line):
146    name = ''
147    width = 0
148    depth = 0
149    ports = ''
150    mask_gran = 0
151    tokens = line.split()
152    i = 0
153    for i in range(0, len(tokens), 2):
154      s = tokens[i]
155      if s == 'name':
156        name = tokens[i+1]
157      elif s == 'width':
158        width = int(tokens[i+1])
159        mask_gran = width # default setting
160      elif s == 'depth':
161        depth = int(tokens[i+1])
162      elif s == 'ports':
163        ports = tokens[i+1].split(',')
164      elif s == 'mask_gran':
165        mask_gran = int(tokens[i+1])
166      else:
167        sys.exit('%s: unknown argument %s' % (sys.argv[0], i))
168    self.conf = (name, width, depth, mask_gran, width//mask_gran, ports)
169  # return (name, width, depth, mask_gran, width//mask_gran, ports)
170
171  def prepare_module(self):
172    (name, width, depth, mask_gran, mask_seg, ports) = self.conf
173    addr_width = max(math.ceil(math.log(depth)/math.log(2)),1)
174
175    mem = VerilogModuleGenerator(name)
176    readports = []
177    writeports = []
178    latchports = []
179    rwports = []
180    maskedports = {}
181
182    for pid, ptype in enumerate(ports):
183      if ptype[0:1] == 'm':
184        ptype = ptype[1:]
185        maskedports[pid] = pid
186
187      if ptype == 'read':
188        prefix = 'R%d_' % len(readports)
189        mem.add_input(1, prefix + "clk")
190        mem.add_input(addr_width, prefix + "addr")
191        mem.add_input(1, prefix + "en")
192        mem.add_output(width, prefix + "data")
193        readports.append(pid)
194      elif ptype == 'write':
195        prefix = 'W%d_' % len(writeports)
196        mem.add_input(1, prefix + "clk")
197        mem.add_input(addr_width, prefix + "addr")
198        mem.add_input(1, prefix + "en")
199        mem.add_input(width, prefix + "data")
200        if pid in maskedports:
201          mem.add_input(mask_seg, prefix + "mask")
202        if not use_latches or pid in maskedports:
203          writeports.append(pid)
204        else:
205          latchports.append(pid)
206      elif ptype == 'rw':
207        prefix = 'RW%d_' % len(rwports)
208        mem.add_input(1, prefix + "clk")
209        mem.add_input(addr_width, prefix + "addr")
210        mem.add_input(1, prefix + "en")
211        mem.add_input(1, prefix + "wmode")
212        if pid in maskedports:
213          mem.add_input(mask_seg, prefix + "wmask")
214        mem.add_input(width, prefix + "wdata")
215        mem.add_output(width, prefix + "rdata")
216        rwports.append(pid)
217      else:
218        sys.exit('%s: unknown port type %s' % (sys.argv[0], ptype))
219    self.mem = mem
220    self.ports_conf = (readports, writeports, latchports, rwports, maskedports)
221
222  def generate(self, blackbox):
223    (name, width, depth, mask_gran, mask_seg, ports) = self.conf
224    addr_width = max(math.ceil(math.log(depth)/math.log(2)),1)
225    mem, (readports, writeports, latchports, rwports, maskedports) = self.mem, self.ports_conf
226
227    nr = len(readports)
228    nw = len(writeports)
229    nrw = len(rwports)
230
231    def emit_read(idx, rw):
232      prefix = ('RW%d_' if rw else 'R%d_') % idx
233      data = ('%srdata' if rw else '%sdata') % prefix
234      en = ('%sen && !%swmode' % (prefix, prefix)) if rw else ('%sen' % prefix)
235      mem.add_decl_reg(1, f"reg_{prefix}ren")
236      mem.add_decl_reg(addr_width, f"reg_{prefix}addr")
237      mem.add_sequential(f"always @(posedge {prefix}clk)")
238      mem.add_sequential(f"  reg_{prefix}ren <= {en};")
239      mem.add_sequential(f"always @(posedge {prefix}clk)")
240      mem.add_sequential(f"  if ({en}) reg_{prefix}addr <= {prefix}addr;")
241      mem.add_combinational("`ifdef RANDOMIZE_GARBAGE_ASSIGN")
242      mem.add_combinational(f"reg [{((width-1)//32+1)*32-1}:0] {prefix}random;")
243      mem.add_combinational(f"`ifdef RANDOMIZE_MEM_INIT")
244      mem.add_combinational(f"  initial begin")
245      mem.add_combinational(f"    #`RANDOMIZE_DELAY begin end")
246      mem.add_combinational('    %srandom = {%s};' % (prefix, ', '.join(['$random'] * ((width-1)//32+1))))
247      mem.add_combinational('    reg_%sren = %srandom[0];' % (prefix, prefix))
248      mem.add_combinational('  end')
249      mem.add_combinational('`endif')
250      mem.add_combinational('always @(posedge %sclk) %srandom <= {%s};' % (prefix, prefix, ', '.join(['$random'] * ((width-1)//32+1))))
251      mem.add_combinational('assign %s = reg_%sren ? ram[reg_%saddr] : %srandom[%d:0];' % (data, prefix, prefix, prefix, width-1))
252      mem.add_combinational('`else')
253      mem.add_combinational('assign %s = ram[reg_%saddr];' % (data, prefix))
254      mem.add_combinational('`endif')
255
256    for idx in range(nr):
257      emit_read(idx, False)
258
259    for idx in range(nrw):
260      emit_read(idx, True)
261
262    for idx in range(len(latchports)):
263      prefix = 'W%d_' % idx
264      mem.add_decl_reg(addr_width, f"latch_{prefix}addr")
265      mem.add_decl_reg(width, f"latch_{prefix}data")
266      mem.add_decl_reg(1, f"latch_{prefix}en")
267      mem.add_combinational('always @(*) begin')
268      mem.add_combinational('  if (!%sclk && %sen) latch_%saddr <= %saddr;' % (prefix, prefix, prefix, prefix))
269      mem.add_combinational('  if (!%sclk && %sen) latch_%sdata <= %sdata;' % (prefix, prefix, prefix, prefix))
270      mem.add_combinational('  if (!%sclk) latch_%sen <= %sen;' % (prefix, prefix, prefix))
271      mem.add_combinational('end')
272      mem.add_combinational('always @(*)')
273      mem.add_combinational('  if (%sclk && latch_%sen)' % (prefix, prefix))
274      mem.add_combinational('    ram[latch_%saddr] <= latch_%sdata;' % (prefix, prefix))
275
276    mem.add_decl_ram(width, "ram", depth)
277    mem.add_decl_line('`ifdef RANDOMIZE_MEM_INIT')
278    mem.add_decl_line('  integer initvar;')
279    mem.add_decl_line('  initial begin')
280    mem.add_decl_line('    #`RANDOMIZE_DELAY begin end')
281    mem.add_decl_line('    for (initvar = 0; initvar < %d; initvar = initvar+1)' % depth)
282    mem.add_decl_line('      ram[initvar] = {%d {$random}};' % ((width-1)//32+1))
283    for idx in range(nr):
284      prefix = 'R%d_' % idx
285      mem.add_decl_line('    reg_%saddr = {%d {$random}};' % (prefix, ((addr_width-1)//32+1)))
286    for idx in range(nrw):
287      prefix = 'RW%d_' % idx
288      mem.add_decl_line('    reg_%saddr = {%d {$random}};' % (prefix, ((addr_width-1)//32+1)))
289    mem.add_decl_line('  end')
290    mem.add_decl_line('`endif')
291
292    mem.add_decl_line("integer i;")
293    for idx in range(nw):
294      prefix = 'W%d_' % idx
295      pid = writeports[idx]
296      mem.add_sequential('always @(posedge %sclk)' % prefix)
297      mem.add_sequential("  if (%sen) begin" % prefix)
298      for i in range(mask_seg):
299        mask = ('if (%smask[%d]) ' % (prefix, i)) if pid in maskedports else ''
300        ram_range = '%d:%d' % ((i+1)*mask_gran-1, i*mask_gran)
301        mem.add_sequential("    %sram[%saddr][%s] <= %sdata[%s];" % (mask, prefix, ram_range, prefix, ram_range))
302      mem.add_sequential("  end")
303    for idx in range(nrw):
304      pid = rwports[idx]
305      prefix = 'RW%d_' % idx
306      mem.add_sequential('always @(posedge %sclk)' % prefix)
307      mem.add_sequential("  if (%sen && %swmode) begin" % (prefix, prefix))
308      if mask_seg > 0:
309        if mask_gran == 1: # If 1 bit mask, use & instead
310          if pid in maskedports:
311            mem.add_sequential("      ram[%saddr] <= (%swmask & %swdata) | (~%swmask & ram[%saddr]);" %(prefix, prefix, prefix, prefix, prefix))
312          else:
313            mem.add_sequential("      ram[%saddr] <= %swdata;" %(prefix, prefix))
314        else:
315          mem.add_sequential("    for (i=0;i<%d;i=i+1) begin" % mask_seg)
316          if pid in maskedports:
317            mem.add_sequential("      if (%swmask[i]) begin" % prefix)
318            mem.add_sequential("        ram[%saddr][i*%d +: %d] <= %swdata[i*%d +: %d];" %(prefix, mask_gran, mask_gran, prefix, mask_gran, mask_gran))
319            mem.add_sequential("      end")
320          else:
321            mem.add_sequential("      ram[%saddr][i*%d +: %d] <= %swdata[i*%d +: %d];" %(prefix, mask_gran, mask_gran, prefix, mask_gran, mask_gran))
322          mem.add_sequential("    end")
323      mem.add_sequential("  end")
324    return mem.generate(blackbox)
325
326
327class SRAM_TSMC28(SRAM):
328  def __init__(self, line):
329    super().__init__(line)
330    self.sub_srams = []
331    if self.__check_subsrams():
332      print(line.strip())
333
334  def __check_subsrams(self):
335    need_split = self.__split()
336    need_reshape  = self.__reshape()
337    assert(not (need_split and need_reshape))
338    return not need_split and not need_reshape
339
340  def __split(self):
341    (name, width, depth, mask_gran, mask_seg, ports) = self.conf
342    '''if ports == ["mrw"] and mask_gran >= 32:
343      new_conf = (name + "_sub", str(depth), str(mask_gran), "rw")
344      line_field = ("name", "depth", "width", "ports")
345      new_line = " ".join(map(lambda x: " ".join(x), zip(line_field, new_conf)))
346      new_sram = SRAM_TSMC28(new_line)
347      self.sub_srams.append(new_sram)
348      reshaper = Spliter(self.conf, new_sram.conf)
349      reshaper.generate(self.mem)
350      return True'''
351    return False
352
353  def __reshape(self):
354    (name, width, depth, mask_gran, mask_seg, ports) = self.conf
355    if width == 2 and depth == 256:
356      new_conf = (name + "_sub", "64", "8", "mwrite,read", "2")
357      line_field = ("name", "depth", "width", "ports", "mask_gran")
358      new_line = " ".join(map(lambda x: " ".join(x), zip(line_field, new_conf)))
359      new_sram = SRAM_TSMC28(new_line)
360      self.sub_srams.append(new_sram)
361      reshaper = Reshaper(self.conf, new_sram.conf)
362      reshaper.generate(self.mem)
363      return True
364    return False
365
366  def __get_tsmc_lib(self):
367    mem, (readports, writeports, latchports, rwports, maskedports) = self.mem, self.ports_conf
368    blackbox = "// tsmc lib here\n"
369    (name, width, depth, mask_gran, mask_seg, _) = self.conf
370    nports = (len(readports), len(writeports), len(rwports))
371    addr_width = max(math.ceil(math.log(depth)/math.log(2)),1)
372    masked = len(maskedports) > 0
373    # from tsmc28_sram import gen_tsmc_ram_1pw, gen_tsmc_ram_1pnw, gen_tsmc_ram_2pw, gen_tsmc_ram_2pnw
374    # if nports == (1, 1, 0):
375    #   if masked:
376    #     blackbox = gen_tsmc_ram_2pw("TS6N28HPCPLVTA64X8M2F", width, mask_gran)
377    #   else:
378    #     blackbox = gen_tsmc_ram_2pnw("TS6N28HPCPLVTA64X14M2F")
379    # elif nports == (0, 0, 1):
380    #   if masked:
381    #     blackbox = gen_tsmc_ram_1pw('TS1N28HPCPLVTB8192X64M8SW', width, mask_gran, addr_width)
382    #   else:
383    #     blackbox = gen_tsmc_ram_1pnw('TS5N28HPCPLVTA64X144M2F', width, addr_width)
384    # else:
385    #   blackbox = "// unknown tsmc lib type\n"
386    return mem.generate(blackbox)
387
388  def generate(self, blackbox, itself_only=False):
389    if itself_only:
390      # generate splits or reshapes
391      if self.sub_srams:
392        return self.mem.generate("")
393      # use empty blackbox
394      elif blackbox:
395        return super().generate(" ")
396      # insert tsmc libs
397      else:
398        return self.__get_tsmc_lib()
399    else:
400      s = self.generate(blackbox, True)
401      for sram in self.sub_srams:
402        s += sram.generate(blackbox)
403      return s
404
405
406def main(args):
407  f = open(args.output_file, "w") if (args.output_file) else None
408  conf_file = args.conf
409  for line in open(conf_file):
410    sram = SRAM(line)
411    if args.tsmc28:
412      sram = SRAM_TSMC28(line)
413    else:
414      sram = SRAM(line)
415    if f is not None:
416        f.write(sram.generate(args.blackbox))
417    else:
418        print(sram.generate(args.blackbox))
419
420
421if __name__ == '__main__':
422  import argparse
423  parser = argparse.ArgumentParser(description='Memory generator for Rocket Chip')
424  parser.add_argument('conf', metavar='.conf file')
425  parser.add_argument('--tsmc28', action='store_true', help='use tsmc28 sram to generate module body')
426  parser.add_argument('--blackbox', '-b', action='store_true', help='set to disable output of module body')
427  #parser.add_argument('--use_latches', '-l', action='store_true', help='set to enable use of latches')
428  parser.add_argument('--output_file', '-o', help='name of output file, default is stdout')
429  args = parser.parse_args()
430  #use_latches = args.use_latches
431  main(args)
432