diff --git a/compiler/base/__init__.py b/compiler/base/__init__.py index df14d00d..3c5d5212 100644 --- a/compiler/base/__init__.py +++ b/compiler/base/__init__.py @@ -16,6 +16,7 @@ from .lef import * from .logical_effort import * from .pin_layout import * from .power_data import * +from .rom_verilog import * from .route import * from .timing_graph import * from .utils import * diff --git a/compiler/base/rom_verilog.py b/compiler/base/rom_verilog.py new file mode 100644 index 00000000..6dbe6cb9 --- /dev/null +++ b/compiler/base/rom_verilog.py @@ -0,0 +1,174 @@ +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2023 Regents of the University of California and The Board +# of Regents for the Oklahoma Agricultural and Mechanical College +# (acting for and on behalf of Oklahoma State University) +# All rights reserved. +# +import math +from openram.tech import spice + + +class rom_verilog: + """ + Create a behavioral Verilog file for simulation. + This is inherited by the rom_base class. + """ + def __init__(self): + pass + + def verilog_write(self, verilog_name): + """ Write a behavioral Verilog model. """ + self.vf = open(verilog_name, "w") + + self.vf.write("// OpenROM ROM model\n") + + #basic info + self.vf.write("// Words: {0}\n".format(self.num_words)) + self.vf.write("// Word size: {0}\n".format(self.word_size)) + self.vf.write("// Word per Row: {0}\n".format(self.words_per_row)) + self.vf.write("// Data Type: {0}\n".format(self.data_type)) + self.vf.write("// Data File: {0}\n".format(self.rom_data)) + + self.vf.write("\n") + + try: + self.vdd_name = spice["power"] + except KeyError: + self.vdd_name = "vdd" + try: + self.gnd_name = spice["ground"] + except KeyError: + self.gnd_name = "gnd" + + #add multiple banks later + self.vf.write("module {0}(\n".format(self.name)) + self.vf.write("`ifdef USE_POWER_PINS\n") + self.vf.write(" {},\n".format(self.vdd_name)) + self.vf.write(" {},\n".format(self.gnd_name)) + self.vf.write("`endif\n") + + for port in self.all_ports: + if port in self.read_ports: + self.vf.write("// Port {0}: R\n".format(port)) + self.vf.write(" clk{0},csb{0},addr{0},dout{0}".format(port)) + # Continue for every port on a new line + if port != self.all_ports[-1]: + self.vf.write(",\n") + self.vf.write("\n );\n\n") + + self.vf.write(" parameter DATA_WIDTH = {0} ;\n".format(self.word_size)) + self.vf.write(" parameter ADDR_WIDTH = {0} ;\n".format(math.ceil(math.log(self.num_words,2)))) + self.vf.write(" parameter ROM_DEPTH = 1 << ADDR_WIDTH;\n") + self.vf.write(" // FIXME: This delay is arbitrary.\n") + self.vf.write(" parameter DELAY = 3 ;\n") + self.vf.write(" parameter VERBOSE = 1 ; //Set to 0 to only display warnings\n") + self.vf.write(" parameter T_HOLD = 1 ; //Delay to hold dout value after posedge. Value is arbitrary\n") + self.vf.write("\n") + + self.vf.write("`ifdef USE_POWER_PINS\n") + self.vf.write(" inout {};\n".format(self.vdd_name)) + self.vf.write(" inout {};\n".format(self.gnd_name)) + self.vf.write("`endif\n") + + for port in self.all_ports: + self.add_inputs_outputs(port) + + self.vf.write("\n") + + # This is the memory array itself + self.vf.write(" reg [DATA_WIDTH-1:0] mem [0:ROM_DEPTH-1];\n\n") + + #write memory init here + self.vf.write(" inital begin\n") + if self.data_type == "bin": + self.vf.write(f" //binary data\n") + self.vf.write(f" $memreadb(\"{self.rom_data}\",mem)\n") + elif self.data_type == "hex": + self.vf.write(f" //hex data\n") + self.vf.write(f" $memreadh(\"{self.rom_data}\",mem)\n") + else: + raise ValueError(f"Data type {self.data_type} is not supported!") + + self.vf.write(" end\n\n") + + for port in self.all_ports: + self.register_inputs(port) + + for port in self.all_ports: + if port in self.read_ports: + self.add_read_block(port) + + self.vf.write("\n") + self.vf.write("endmodule\n") + self.vf.close() + + def register_inputs(self, port): + """ + Register the control signal, address and data inputs. + """ + self.add_regs(port) + self.add_flops(port) + + def add_regs(self, port): + """ + Create the input regs for the given port. + """ + self.vf.write(" reg csb{0}_reg;\n".format(port)) + if port in self.read_ports: + self.vf.write(" reg [DATA_WIDTH-1:0] dout{0};\n".format(port)) + + def add_flops(self, port): + """ + Add the flop behavior logic for a port. + """ + self.vf.write("\n") + self.vf.write(" // All inputs are registers\n") + self.vf.write(" always @(posedge clk{0})\n".format(port)) + self.vf.write(" begin\n") + self.vf.write(" csb{0}_reg = csb{0};\n".format(port)) + self.vf.write(" addr{0}_reg = addr{0};\n".format(port)) + if port in self.read_ports: + self.add_write_read_checks(port) + + if port in self.read_ports: + self.vf.write(" #(T_HOLD) dout{0} = {1}'bx;\n".format(port, self.word_size)) + self.vf.write(" if ( !csb{0}_reg && VERBOSE ) \n".format(port)) + self.vf.write(" $display($time,\" Reading %m addr{0}=%b dout{0}=%b\",addr{0}_reg,mem[addr{0}_reg]);\n".format(port)) + + self.vf.write(" end\n\n") + + def add_inputs_outputs(self, port): + """ + Add the module input and output declaration for a port. + """ + self.vf.write(" input clk{0}; // clock\n".format(port)) + self.vf.write(" input csb{0}; // active low chip select\n".format(port)) + + self.vf.write(" input [ADDR_WIDTH-1:0] addr{0};\n".format(port)) + if port in self.read_ports: + self.vf.write(" output [DATA_WIDTH-1:0] dout{0};\n".format(port)) + + def add_write_block(self, port): + """ + ROM does not take writes thus this function does nothing + """ + self.vf.write("\n") + def add_read_block(self, port): + """ + Add a read port block. + """ + self.vf.write("\n") + self.vf.write(" // Memory Read Block Port {0}\n".format(port)) + self.vf.write(" // Read Operation : When web{0} = 1, csb{0} = 0\n".format(port)) + self.vf.write(" always @ (negedge clk{0})\n".format(port)) + self.vf.write(" begin : MEM_READ{0}\n".format(port)) + self.vf.write(" if (!csb{0}_reg)\n".format(port)) + self.vf.write(" dout{0} <= #(DELAY) mem[addr{0}_reg];\n".format(port)) + self.vf.write(" end\n") + + def add_write_read_checks(self, rport): + """ + Since ROMs dont have write ports this does nothing + """ + pass diff --git a/compiler/modules/rom_bank.py b/compiler/modules/rom_bank.py index f4940338..e4d22f1c 100644 --- a/compiler/modules/rom_bank.py +++ b/compiler/modules/rom_bank.py @@ -10,13 +10,14 @@ import datetime from math import ceil, log from openram.base import vector from openram.base import design +from openram.base import rom_verilog from openram import OPTS, print_time from openram.sram_factory import factory from openram.tech import drc, layer, parameter from openram.router import router_tech -class rom_bank(design): +class rom_bank(design,rom_verilog): """ Rom data bank with row and column decoder + control logic @@ -509,4 +510,4 @@ class rom_bank(design): rtr=router(layers=self.m3_stack, design=self, bbox=bbox) - rtr.escape_route(pins_to_route) \ No newline at end of file + rtr.escape_route(pins_to_route) diff --git a/compiler/modules/sram_1bank.py b/compiler/modules/sram_1bank.py index 8cc7cd8c..2302a162 100644 --- a/compiler/modules/sram_1bank.py +++ b/compiler/modules/sram_1bank.py @@ -25,9 +25,13 @@ class sram_1bank(design, verilog, lef): Procedures specific to a one bank SRAM. """ def __init__(self, name, sram_config): + print("sram_1bank debug: init") design.__init__(self, name) + print("sram_1bank debug: design init") lef.__init__(self, ["m1", "m2", "m3", "m4"]) + print("sram_1bank debug: lef init") verilog.__init__(self) + print("sram_1bank debug: verilog init") self.sram_config = sram_config sram_config.set_local_config(self) diff --git a/compiler/rom.py b/compiler/rom.py index bc81dfd2..37954457 100644 --- a/compiler/rom.py +++ b/compiler/rom.py @@ -39,7 +39,7 @@ class rom(): from openram.base import design design.name_map=[] - debug.info(2, "create rom of size {0} with {1} num of words".format(self.word_size, + debug.print_raw("create rom of word size {0} with {1} num of words".format(self.word_size, self.num_words)) start_time = datetime.datetime.now() @@ -48,7 +48,7 @@ class rom(): import openram.modules.rom_bank as rom self.r = rom(name, rom_config) - + self.r.create_netlist() if not OPTS.netlist_only: self.r.create_layout() @@ -138,20 +138,22 @@ class rom(): # Write the config file + # Should also save the provided data file start_time = datetime.datetime.now() from shutil import copyfile copyfile(OPTS.config_file, OPTS.output_path + OPTS.output_name + '.py') + copyfile(self.rom_data, OPTS.output_path + self.rom_data) debug.print_raw("Config: Writing to {0}".format(OPTS.output_path + OPTS.output_name + '.py')) print_time("Config", datetime.datetime.now(), start_time) # TODO: Write the datasheet - # TODO: Write a verilog model - # start_time = datetime.datetime.now() - # vname = OPTS.output_path + self.r.name + '.v' - # debug.print_raw("Verilog: Writing to {0}".format(vname)) - # self.verilog_write(vname) - # print_time("Verilog", datetime.datetime.now(), start_time) + #Write a verilog model + start_time = datetime.datetime.now() + vname = OPTS.output_path + self.r.name + '.v' + debug.print_raw("Verilog: Writing to {0}".format(vname)) + self.verilog_write(vname) + print_time("Verilog", datetime.datetime.now(), start_time) # Write out options if specified if OPTS.output_extended_config: