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