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