Merge branch 'dev' into delay_ctrl

This commit is contained in:
Sam Crow
2023-07-06 08:45:03 -07:00
20 changed files with 365 additions and 42 deletions
+1
View File
@@ -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 *
+173
View File
@@ -0,0 +1,173 @@
# 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(f" initial begin\n")
if self.data_type == "bin":
self.vf.write(f" $readmemb(\"{self.rom_data}\",mem,0,ROM_DEPTH-1);\n")
elif self.data_type == "hex":
self.vf.write(f" $readmemh(\"{self.rom_data}\",mem,0, ROM_DEPTH-1);\n")
else:
raise ValueError(f"Data type: {self.data_type} is not supported!")
self.vf.write(f" 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))
self.vf.write(" reg [ADDR_WIDTH-1:0] addr{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
+3 -2
View File
@@ -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)
rtr.escape_route(pins_to_route)
-1
View File
@@ -28,7 +28,6 @@ class sram_1bank(design, verilog, lef):
design.__init__(self, name)
lef.__init__(self, ["m1", "m2", "m3", "m4"])
verilog.__init__(self)
self.sram_config = sram_config
sram_config.set_local_config(self)
+1
View File
@@ -58,6 +58,7 @@ class options(optparse.Values):
###################
rom_endian = "little"
rom_data = None
data_type = "bin"
strap_spacing = 8
scramble_bits = True
+11 -8
View File
@@ -26,7 +26,8 @@ class rom():
words_per_row=OPTS.words_per_row,
rom_endian=OPTS.rom_endian,
scramble_bits=OPTS.scramble_bits,
strap_spacing=OPTS.strap_spacing)
strap_spacing=OPTS.strap_spacing,
data_type=OPTS.data_type)
if name is None:
name = OPTS.output_name
@@ -38,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()
@@ -137,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:
+37 -14
View File
@@ -16,14 +16,14 @@ from openram import OPTS
class rom_config:
""" This is a structure that is used to hold the ROM configuration options. """
def __init__(self, word_size, rom_data, words_per_row=None, rom_endian="little", scramble_bits=True, strap_spacing=8):
def __init__(self, word_size, rom_data, words_per_row=None, rom_endian="little", scramble_bits=True, strap_spacing=8, data_type="hex"):
self.word_size = word_size
self.word_bits = self.word_size * 8
self.rom_data = rom_data
self.strap_spacing = strap_spacing
# TODO: This currently does nothing. It should change the behavior of the chunk funciton.
self.endian = rom_endian
self.data_type = data_type
# This should pretty much always be true. If you want to make silicon art you might set to false
self.scramble_bits = scramble_bits
# This will get over-written when we determine the organization
@@ -57,18 +57,12 @@ class rom_config:
def compute_sizes(self):
""" Computes the organization of the memory using data size by trying to make it a rectangle."""
# Read data as hexidecimal text file
hex_file = open(self.rom_data, 'r')
hex_data = hex_file.read()
# Convert from hex into an int
data_int = int(hex_data, 16)
# Then from int into a right aligned, zero padded string
bin_string = bin(data_int)[2:].zfill(len(hex_data) * 4)
# Then turn the string into a list of ints
bin_data = list(bin_string)
raw_data = [int(x) for x in bin_data]
if self.data_type == "hex":
raw_data = self.read_data_hex()
elif self.data_type == "bin":
raw_data = self.read_data_bin()
else:
debug.error(f"Invalid input data type: {self.data_type}", -1)
# data size in bytes
data_size = len(raw_data) / 8
@@ -93,6 +87,35 @@ class rom_config:
OPTS.words_per_row = self.words_per_row
debug.info(1, "Read rom data file: length {0} bytes, {1} words, set number of cols to {2}, rows to {3}, with {4} words per row".format(data_size, self.num_words, self.cols, self.rows, self.words_per_row))
def read_data_hex(self) -> List[int]:
# Read data as hexidecimal text file
with open(self.rom_data, 'r') as hex_file:
hex_data = hex_file.read()
# Convert from hex into an int
data_int = int(hex_data, 16)
# Then from int into a right aligned, zero padded string
bin_string = bin(data_int)[2:].zfill(len(hex_data) * 4)
# Then turn the string into a list of ints
bin_data = list(bin_string)
raw_data = [int(x) for x in bin_data]
return raw_data
def read_data_bin(self) -> List[int]:
# Read data as a binary file
with open(self.rom_data, 'rb') as bin_file:
bin_data = bin_file.read()
# Convert from a list of bytes to a single string of bits
bin_string = "".join(f"{n:08b}" for n in bin_data)
# Then turn the string into a list of ints
bin_data = list(bin_string)
raw_data = [int(x) for x in bin_data]
return raw_data
def chunk_data(self, raw_data: List[int]):
"""