vlsi_mem_gen 17 KB

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