diff --git a/compiler/characterizer/functional.py b/compiler/characterizer/functional.py index 6dcb54e0..60ebd339 100644 --- a/compiler/characterizer/functional.py +++ b/compiler/characterizer/functional.py @@ -102,7 +102,6 @@ class functional(simulation): # Write at least once addr = self.gen_addr() word = self.gen_data() - # print("write", self.t_current, addr, word) comment = self.gen_cycle_comment("write", word, addr, self.wmask, 0, self.t_current) self.add_write(comment, addr, word, self.wmask, 0) self.stored_words[addr] = word @@ -113,9 +112,8 @@ class functional(simulation): if port in self.write_ports: self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port) else: - # print("read", self.t_current, addr, word) comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current) - self.add_read_one_port(comment, addr, rw_read_din_data, "1"*self.num_wmasks, port) + self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port) self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) check += 1 self.cycle_times.append(self.t_current) @@ -150,7 +148,7 @@ class functional(simulation): self.stored_words[addr] = word w_addrs.append(addr) elif op == "partial_write": - #write only to a word that's been written to + # write only to a word that's been written to (addr,old_word) = self.get_data() word = self.gen_data() wmask = self.gen_wmask() @@ -177,7 +175,7 @@ class functional(simulation): self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port) else: comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current) - self.add_read_one_port(comment, addr, rw_read_din_data, "1"*self.num_wmasks, port) + self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port) self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) check += 1 @@ -259,7 +257,7 @@ class functional(simulation): def get_data(self): """ Gets an available address and corresponding word. """ - # Currently unused but may need later depending on how the functional test develops + # Used for write masks since they should be writing to previously written addresses addr = random.choice(list(self.stored_words.keys())) word = self.stored_words[addr] return (addr,word) diff --git a/compiler/modules/port_data.py b/compiler/modules/port_data.py index 3e4caf57..15e84f2b 100644 --- a/compiler/modules/port_data.py +++ b/compiler/modules/port_data.py @@ -308,23 +308,23 @@ class port_data(design.design): """ Placing Sense amp """ self.sense_amp_array_inst.place(offset=offset, mirror="MX") - + def create_write_driver_array(self): """ Creating Write Driver """ - self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port), + self.write_driver_array_inst = self.add_inst(name="write_driver_array{}".format(self.port), mod=self.write_driver_array) temp = [] for bit in range(self.word_size): temp.append("din_{}".format(bit)) - for bit in range(self.word_size): + for bit in range(self.word_size): if (self.words_per_row == 1): - temp.append(self.bl_names[self.port]+"_{0}".format(bit)) - temp.append(self.br_names[self.port]+"_{0}".format(bit)) + temp.append(self.bl_names[self.port] + "_{0}".format(bit)) + temp.append(self.br_names[self.port] + "_{0}".format(bit)) else: - temp.append(self.bl_names[self.port]+"_out_{0}".format(bit)) - temp.append(self.br_names[self.port]+"_out_{0}".format(bit)) + temp.append(self.bl_names[self.port] + "_out_{0}".format(bit)) + temp.append(self.br_names[self.port] + "_out_{0}".format(bit)) if self.write_size is not None: for i in range(self.num_wmasks): @@ -336,10 +336,15 @@ class port_data(design.design): self.connect_inst(temp) + def place_write_driver_array(self, offset): + """ Placing Write Driver """ + self.write_driver_array_inst.place(offset=offset, mirror="MX") + + def create_write_mask_and_array(self): - """ Creating Write Masks """ + """ Creating Write Mask AND Array """ self.write_mask_and_array_inst = self.add_inst(name="write_mask_and_array{}".format(self.port), - mod=self.write_mask_and_array) + mod=self.write_mask_and_array) temp = [] for bit in range(self.num_wmasks): @@ -351,10 +356,10 @@ class port_data(design.design): self.connect_inst(temp) - def place_write_driver_array(self, offset): - """ Placing Write Driver """ - self .write_driver_array_inst.place(offset=offset, mirror="MX") - + def place_write_mask_and_array(self, offset): + """ Placing Write Mask AND array """ + self.write_mask_and_array_inst.place(offset=offset, mirror="MX") + def compute_instance_offsets(self): """ @@ -366,41 +371,41 @@ class port_data(design.design): vertical_port_order.append(self.column_mux_array_inst) vertical_port_order.append(self.sense_amp_array_inst) vertical_port_order.append(self.write_driver_array_inst) + vertical_port_order.append(self.write_mask_and_array_inst) # Add one column for the the RBL - if self.has_rbl() and self.port==0: + if self.has_rbl() and self.port == 0: x_offset = self.bitcell.width else: x_offset = 0 - - vertical_port_offsets = 4*[None] + + vertical_port_offsets = 5 * [None] self.width = x_offset self.height = 0 - for i,p in enumerate(vertical_port_order): - if p==None: + for i, p in enumerate(vertical_port_order): + if p == None: continue self.height += (p.height + self.m2_gap) self.width = max(self.width, p.width) - vertical_port_offsets[i]=vector(x_offset,self.height) + vertical_port_offsets[i] = vector(x_offset, self.height) # Reversed order + self.write_mask_and_offset = vertical_port_offsets[4] self.write_driver_offset = vertical_port_offsets[3] self.sense_amp_offset = vertical_port_offsets[2] self.column_mux_offset = vertical_port_offsets[1] self.precharge_offset = vertical_port_offsets[0] # Shift the precharge left if port 0 - if self.precharge_offset and self.port==0: - self.precharge_offset -= vector(x_offset,0) - + if self.precharge_offset and self.port == 0: + self.precharge_offset -= vector(x_offset, 0) - - - def place_instances(self): """ Place the instances. """ - # These are fixed in the order: write driver, sense amp, clumn mux, precharge, + # These are fixed in the order: write mask ANDs, write driver, sense amp, column mux, precharge, # even if the item is not used in a given port (it will be None then) + if self.write_mask_and_offset: + self.place_write_mask_and_array(self.write_mask_and_offset) if self.write_driver_offset: self.place_write_driver_array(self.write_driver_offset) if self.sense_amp_offset: @@ -410,6 +415,7 @@ class port_data(design.design): if self.column_mux_offset: self.place_column_mux_array(self.column_mux_offset) + def route_sense_amp_out(self, port): """ Add pins for the sense amp output """ @@ -420,7 +426,8 @@ class port_data(design.design): offset=data_pin.center(), height=data_pin.height(), width=data_pin.width()) - + + def route_write_driver_in(self, port): """ Connecting write driver """ @@ -428,7 +435,25 @@ class port_data(design.design): data_name = "data_{}".format(row) din_name = "din_{}".format(row) self.copy_layout_pin(self.write_driver_array_inst, data_name, din_name) - + + + def route_write_mask_and_out(self, port): + """ Add pins for the write mask and array output """ + + for bit in range(self.num_wmasks): + wdriver_sel_pin = self.write_mask_and_array_inst.get_pin("wdriver_sel_{}".format(bit)) + self.add_layout_pin_rect_center(text="wdriver_sel_{0}".format(bit), + layer=wdriver_sel_pin.layer, + offset=wdriver_sel_pin.center(), + height=wdriver_sel_pin.height(), + width=wdriver_sel_pin.width()) + + # for bit in range(self.num_wmasks): + # print(bit) + # wdriver_sel_name = "wdriver_sel_{}".format(bit) + # self.copy_layout_pin(self.write_mask_and_array_inst, wdriver_sel_name) + + def route_column_mux_to_precharge_array(self, port): """ Routing of BL and BR between col mux and precharge array """ @@ -503,6 +528,14 @@ class port_data(design.design): self.channel_route_bitlines(inst1=inst1, inst2=inst2, num_bits=self.word_size) + def route_write_mask_and_to_write_driver(self,port): + """ Routing of wdriver_sel_{} between write mask AND and write driver """ + inst1 = self.write_mask_and_array_inst + inst2 = self.write_driver_array_inst + + inst1_wdriver_sel_name = "wdriver_sel_{}" + start_bit=0 + def route_bitline_pins(self): """ Add the bitline pins for the given port """ @@ -524,7 +557,7 @@ class port_data(design.design): self.copy_layout_pin(self.precharge_array_inst, "bl_{}".format(bit+bit_offset), "bl_{}".format(bit)) self.copy_layout_pin(self.precharge_array_inst, "br_{}".format(bit+bit_offset), "br_{}".format(bit)) else: - debug.error("Didn't find precharge arra.") + debug.error("Didn't find precharge array.") def route_control_pins(self): """ Add the control pins: s_en, p_en_bar, w_en """ @@ -537,7 +570,13 @@ class port_data(design.design): if self.sense_amp_array_inst: self.copy_layout_pin(self.sense_amp_array_inst, "en", "s_en") if self.write_driver_array_inst: - self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en") + if self.write_mask_and_array_inst: + for bit in range(self.num_wmasks): +s self.copy_layout_pin(self.write_driver_array_inst, "en_{}".format(bit), "wdriver_sel_{}".format(bit)) + else: + self.copy_layout_pin(self.write_driver_array_inst, "en", "w_en") + if self.write_mask_and_array_inst: + self.copy_layout_pin(self.write_mask_and_array_inst, "en", "w_en") def channel_route_bitlines(self, inst1, inst2, num_bits, diff --git a/compiler/modules/write_driver_array.py b/compiler/modules/write_driver_array.py index 7da57b37..cddd81e4 100644 --- a/compiler/modules/write_driver_array.py +++ b/compiler/modules/write_driver_array.py @@ -95,6 +95,7 @@ class write_driver_array(design.design): "br_{0}".format(index), "en_{0}".format(windex), "vdd", "gnd"]) w+=1 + # when w equals write size, the next en pin can be connected since we are now at the next wmask bit if w == self.write_size: w = 0 windex+=1 @@ -149,12 +150,19 @@ class write_driver_array(design.design): self.add_layout_pin_rect_center(text=n, layer="metal3", offset=pin_pos) - - self.add_layout_pin(text="en", - layer="metal1", - offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), - width=self.width, - height=drc('minwidth_metal1')) + if self.write_size is not None: + for bit in range(self.num_wmasks): + self.add_layout_pin(text="en_{}".format(bit), + layer="metal1", + offset=self.driver_insts[bit*(self.write_size-1)].get_pin("en").ll().scale(0,1), + width=self.width, + height=drc('minwidth_metal1')) + else: + self.add_layout_pin(text="en", + layer="metal1", + offset=self.driver_insts[0].get_pin("en").ll().scale(0,1), + width=self.width, + height=drc('minwidth_metal1')) diff --git a/compiler/tests/18_port_data_wmask_test.py b/compiler/tests/18_port_data_wmask_test.py new file mode 100644 index 00000000..abc4f5d0 --- /dev/null +++ b/compiler/tests/18_port_data_wmask_test.py @@ -0,0 +1,114 @@ +#!/usr/bin/env python3 +# See LICENSE for licensing information. +# +# Copyright (c) 2016-2019 Regents of the University of California +# All rights reserved. +# +import unittest +from testutils import * +import sys, os + +sys.path.append(os.getenv("OPENRAM_HOME")) +import globals +from globals import OPTS +from sram_factory import factory +import debug + + +class port_data_test(openram_test): + + def runTest(self): + globals.init_openram("config_{0}".format(OPTS.tech_name)) + from sram_config import sram_config + + c = sram_config(word_size=8, + write_size=4, + num_words=16) + + c.words_per_row = 1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 32 + c.words_per_row = 2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.num_words = 64 + c.words_per_row = 4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + c.word_size = 2 + c.num_words = 128 + c.words_per_row = 8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + + OPTS.bitcell = "bitcell_1w_1r" + OPTS.num_rw_ports = 0 + OPTS.num_r_ports = 1 + OPTS.num_w_ports = 1 + + c.num_words = 16 + c.words_per_row = 1 + factory.reset() + c.recompute_sizes() + debug.info(1, "No column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words = 32 + c.words_per_row = 2 + factory.reset() + c.recompute_sizes() + debug.info(1, "Two way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.num_words = 64 + c.words_per_row = 4 + factory.reset() + c.recompute_sizes() + debug.info(1, "Four way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + c.word_size = 2 + c.num_words = 128 + c.words_per_row = 8 + factory.reset() + c.recompute_sizes() + debug.info(1, "Eight way column mux") + a = factory.create("port_data", sram_config=c, port=0) + self.local_check(a) + a = factory.create("port_data", sram_config=c, port=1) + self.local_check(a) + + globals.end_openram() + + +# run the test from the command line +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main(testRunner=debugTestRunner()) \ No newline at end of file diff --git a/compiler/tests/22_psram_wmask_func_test.py b/compiler/tests/22_sram_wmask_1w_1r_func_test.py similarity index 95% rename from compiler/tests/22_psram_wmask_func_test.py rename to compiler/tests/22_sram_wmask_1w_1r_func_test.py index eafeb0a9..2ee79750 100755 --- a/compiler/tests/22_psram_wmask_func_test.py +++ b/compiler/tests/22_sram_wmask_1w_1r_func_test.py @@ -17,8 +17,8 @@ from sram_factory import factory import debug -# @unittest.skip("SKIPPING psram_wmask_func_test") -class psram_wmask_func_test(openram_test): +# @unittest.skip("SKIPPING sram_wmask_1w_1r_func_test") +class sram_wmask_1w_1r_func_test(openram_test): def runTest(self): globals.init_openram("config_{0}".format(OPTS.tech_name))