2018-05-12 01:32:00 +02:00
|
|
|
#!/usr/bin/env python3
|
2019-04-26 21:21:50 +02:00
|
|
|
# See LICENSE for licensing information.
|
|
|
|
|
#
|
2019-06-14 17:43:41 +02:00
|
|
|
# Copyright (c) 2016-2019 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.
|
2019-04-26 21:21:50 +02:00
|
|
|
#
|
2016-11-08 18:57:35 +01:00
|
|
|
import unittest
|
2019-05-31 19:51:42 +02:00
|
|
|
from testutils import *
|
2016-11-08 18:57:35 +01:00
|
|
|
import sys,os
|
2019-05-31 19:51:42 +02:00
|
|
|
sys.path.append(os.getenv("OPENRAM_HOME"))
|
2016-11-08 18:57:35 +01:00
|
|
|
import globals
|
2017-11-16 22:52:58 +01:00
|
|
|
from globals import OPTS
|
2019-03-06 23:12:24 +01:00
|
|
|
from sram_factory import factory
|
2016-11-08 18:57:35 +01:00
|
|
|
import debug
|
|
|
|
|
|
2018-10-23 21:55:54 +02:00
|
|
|
@unittest.skip("SKIPPING 26_pex_test")
|
2018-01-30 01:59:29 +01:00
|
|
|
class sram_func_test(openram_test):
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def runTest(self):
|
2019-11-17 01:44:31 +01:00
|
|
|
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
|
2019-11-15 19:47:59 +01:00
|
|
|
globals.init_openram(config_file)
|
2018-07-11 21:00:15 +02:00
|
|
|
|
|
|
|
|
OPTS.use_pex = True
|
|
|
|
|
|
|
|
|
|
# This is a hack to reload the characterizer __init__ with the spice version
|
|
|
|
|
from importlib import reload
|
|
|
|
|
import characterizer
|
|
|
|
|
reload(characterizer)
|
|
|
|
|
from characterizer import setup_hold
|
|
|
|
|
if not OPTS.spice_exe:
|
|
|
|
|
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
self.func_test(bank_num=1)
|
|
|
|
|
self.func_test(bank_num=2)
|
|
|
|
|
self.func_test(bank_num=4)
|
|
|
|
|
|
2016-11-11 23:05:14 +01:00
|
|
|
globals.end_openram()
|
|
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
def func_test(self, bank_num):
|
|
|
|
|
|
|
|
|
|
import sram
|
|
|
|
|
import tech
|
|
|
|
|
|
|
|
|
|
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
|
2018-01-20 02:23:38 +01:00
|
|
|
s = sram.sram(word_size=OPTS.word_size,
|
|
|
|
|
num_words=OPTS.num_words,
|
|
|
|
|
num_banks=OPTS.num_banks,
|
2016-11-08 18:57:35 +01:00
|
|
|
name="test_sram1")
|
|
|
|
|
|
|
|
|
|
tempspice = OPTS.openram_temp + "temp.sp"
|
|
|
|
|
tempgds = OPTS.openram_temp + "temp.gds"
|
|
|
|
|
|
|
|
|
|
s.sp_write(tempspice)
|
|
|
|
|
s.gds_write(tempgds)
|
|
|
|
|
|
2017-11-15 01:24:26 +01:00
|
|
|
self.assertFalse(verify.run_drc(s.name, tempgds))
|
|
|
|
|
self.assertFalse(verify.run_lvs(s.name, tempgds, tempspice))
|
|
|
|
|
self.assertFalse(verify.run_pex(s.name, tempgds,
|
2016-11-08 18:57:35 +01:00
|
|
|
tempspice, output=OPTS.openram_temp + "temp_pex.sp"))
|
|
|
|
|
|
|
|
|
|
import sp_file
|
|
|
|
|
stimulus_file = OPTS.openram_temp + "stimulus.sp"
|
|
|
|
|
a_stimulus = sp_file.sp_file(stimulus_file)
|
|
|
|
|
self.write_stimulus(a_stimulus)
|
|
|
|
|
|
|
|
|
|
simulator_file = OPTS.openram_temp + "simulator.sp"
|
|
|
|
|
a_simulator = sp_file.sp_file(simulator_file)
|
|
|
|
|
self.write_simulator(a_simulator)
|
|
|
|
|
|
|
|
|
|
result_file = OPTS.openram_temp + "result"
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
2017-11-23 00:57:29 +01:00
|
|
|
if OPTS.spice_name == "hspice":
|
2017-06-02 20:11:57 +02:00
|
|
|
cmd = "hspice -mt 2 -i {0} > {1} ".format(
|
2016-11-08 18:57:35 +01:00
|
|
|
simulator_file, result_file)
|
|
|
|
|
else:
|
|
|
|
|
cmd = "ngspice -b -i {0} > {1} ".format(
|
|
|
|
|
simulator_file, result_file)
|
|
|
|
|
os.system(cmd)
|
|
|
|
|
|
|
|
|
|
import re
|
|
|
|
|
sp_result = open(result_file, "r")
|
|
|
|
|
contents = sp_result.read()
|
|
|
|
|
key = "vr1"
|
|
|
|
|
val = re.search(
|
|
|
|
|
r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents)
|
|
|
|
|
val = val.group(3)
|
|
|
|
|
value1 = float(self.convert_voltage_unit(val))
|
|
|
|
|
|
|
|
|
|
key = "vr2"
|
|
|
|
|
val = re.search(
|
|
|
|
|
r"{0}(\s*)=(\s*)(\d*(.).*)(\s*)(from)".format(key), contents)
|
|
|
|
|
val = val.group(3)
|
|
|
|
|
value2 = float(self.convert_voltage_unit(val))
|
|
|
|
|
|
|
|
|
|
self.assertTrue(round(value1) > 0.5 * tech.spice["supply_voltage"])
|
|
|
|
|
self.assertTrue(round(value2) < 0.5 * tech.spice["supply_voltage"])
|
|
|
|
|
|
2018-07-11 01:39:32 +02:00
|
|
|
|
2016-11-08 18:57:35 +01:00
|
|
|
|
|
|
|
|
def convert_voltage_unit(self, string):
|
|
|
|
|
newstring = ""
|
|
|
|
|
for letter in string:
|
|
|
|
|
if letter == "m":
|
|
|
|
|
letter = "10e-3"
|
|
|
|
|
elif letter == "u":
|
|
|
|
|
letter = "10e-6"
|
|
|
|
|
else:
|
|
|
|
|
letter = letter
|
|
|
|
|
newstring = str(newstring) + str(letter)
|
|
|
|
|
return newstring
|
|
|
|
|
|
|
|
|
|
def convert_time_unit(self, string):
|
|
|
|
|
newstring = ""
|
|
|
|
|
for letter in string:
|
|
|
|
|
if letter == "f":
|
|
|
|
|
letter = "10e-15"
|
|
|
|
|
elif letter == "p":
|
|
|
|
|
letter = "10e-12"
|
|
|
|
|
elif letter == "n":
|
|
|
|
|
letter = "10e-9"
|
|
|
|
|
elif letter == "u":
|
|
|
|
|
letter = "10e-6"
|
|
|
|
|
elif letter == "m":
|
|
|
|
|
letter = "10e-3"
|
|
|
|
|
else:
|
|
|
|
|
letter = letter
|
|
|
|
|
newstring = str(newstring) + str(letter)
|
|
|
|
|
return newstring
|
|
|
|
|
|
|
|
|
|
def write_simulator(self, sim_file):
|
|
|
|
|
sim_file.write("\n")
|
|
|
|
|
import tech
|
|
|
|
|
time_step = tech.spice["clock_period"]
|
|
|
|
|
for model in tech.spice["fet_models"]:
|
|
|
|
|
sim_file.write(".inc " + str(model) + "\n")
|
|
|
|
|
sim_file.write(".inc stimulus.sp\n")
|
|
|
|
|
sim_file.write(".inc temp_pex.sp\n")
|
|
|
|
|
sim_file.write(".options post runlvl=6\n")
|
|
|
|
|
sim_file.write("\n")
|
|
|
|
|
|
|
|
|
|
sim_file.write(
|
|
|
|
|
"Xsource DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss source\n")
|
|
|
|
|
sim_file.write(
|
|
|
|
|
"Xsram DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb OEb clk vdd vss test_sram1\n")
|
|
|
|
|
sim_file.write("\n")
|
|
|
|
|
|
|
|
|
|
sim_file.write(".MEASURE TRAN vr1 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format(
|
|
|
|
|
4.5 * tech.spice["clock_period"], 5 * tech.spice["clock_period"]))
|
|
|
|
|
sim_file.write(".MEASURE TRAN vr2 AVG V(DATA[0]) FROM ={0}ns TO ={1}ns\n".format(
|
|
|
|
|
9.5 * tech.spice["clock_period"], 10 * tech.spice["clock_period"]))
|
|
|
|
|
sim_file.write("\n")
|
|
|
|
|
|
2017-11-23 00:57:29 +01:00
|
|
|
if OPTS.spice_name in ["hspice","xa"]:
|
2016-11-08 18:57:35 +01:00
|
|
|
sim_file.write(".probe v(x*.*)\n")
|
|
|
|
|
sim_file.write(".tran 0.1ns {0}ns\n".format(
|
|
|
|
|
10 * tech.spice["clock_period"]))
|
|
|
|
|
sim_file.write(".end\n")
|
|
|
|
|
else:
|
|
|
|
|
sim_file.write(
|
|
|
|
|
".meas tran DELAY1.0 TRIG v(clk) VAL=0.5 RISE=6 TARG v(DATA[0]) VAL=0.5 TD=0.5n RISE=1\n")
|
|
|
|
|
sim_file.write(".tran 0.1ns {0}ns\n".format(
|
|
|
|
|
10 * tech.spice["clock_period"]))
|
|
|
|
|
sim_file.write(".control\n")
|
|
|
|
|
sim_file.write("run\n")
|
|
|
|
|
#sim_file.write("plot CSb WEb OEb \n")
|
|
|
|
|
#sim_file.write("plot clk DATA0 \n")
|
|
|
|
|
sim_file.write("quit\n")
|
|
|
|
|
sim_file.write(".endc\n")
|
|
|
|
|
sim_file.write(".end\n")
|
|
|
|
|
sim_file.file.close()
|
|
|
|
|
|
|
|
|
|
def write_stimulus(self, sti_file):
|
|
|
|
|
import tech
|
|
|
|
|
import sp_file
|
|
|
|
|
sti_file.write(
|
|
|
|
|
".subckt source DATA[0] ADDR[0] ADDR[1] ADDR[2] ADDR[3] CSb WEb WEb_inv OEb clk vdd vss\n")
|
|
|
|
|
|
|
|
|
|
time_step = tech.spice["clock_period"]
|
|
|
|
|
|
|
|
|
|
clk = sp_file.PWL(name="clk", port=["clk", "0"])
|
|
|
|
|
for i in range(0, 11):
|
|
|
|
|
clk.write_pulse(i * time_step, time_step, "UP")
|
|
|
|
|
clk.write_to_sp(sti_file)
|
|
|
|
|
|
|
|
|
|
WEB_inv = sp_file.PWL(name="WEb_inv", port=["WEb_inv", "0"])
|
|
|
|
|
WEB = sp_file.PWL(name="WEB", port=["WEb", "0"])
|
|
|
|
|
OEb = sp_file.PWL(name="OEb", port=["OEb", "0"])
|
|
|
|
|
CSb = sp_file.PWL(name="CSb", port=["CSb", "0"])
|
|
|
|
|
|
|
|
|
|
# write
|
|
|
|
|
CSb.write_pulse(0.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB.write_pulse(0.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB_inv.write_pulse(0.75 * time_step, time_step, "UP")
|
|
|
|
|
CSb.write_pulse(1.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB.write_pulse(1.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB_inv.write_pulse(1.75 * time_step, time_step, "UP")
|
|
|
|
|
|
|
|
|
|
# read
|
|
|
|
|
OEb.write_pulse(3.75 * time_step, time_step, "DN")
|
|
|
|
|
CSb.write_pulse(3.75 * time_step, time_step, "DN")
|
|
|
|
|
|
|
|
|
|
# write
|
|
|
|
|
CSb.write_pulse(5.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB.write_pulse(5.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB_inv.write_pulse(5.75 * time_step, time_step, "UP")
|
|
|
|
|
CSb.write_pulse(6.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB.write_pulse(6.75 * time_step, time_step, "DN")
|
|
|
|
|
WEB_inv.write_pulse(6.75 * time_step, time_step, "UP")
|
|
|
|
|
|
|
|
|
|
# read
|
|
|
|
|
OEb.write_pulse(8.75 * time_step, time_step, "DN")
|
|
|
|
|
CSb.write_pulse(8.75 * time_step, time_step, "DN")
|
|
|
|
|
|
|
|
|
|
CSb.write_to_sp(sti_file)
|
|
|
|
|
WEB.write_to_sp(sti_file)
|
|
|
|
|
WEB_inv.write_to_sp(sti_file)
|
|
|
|
|
OEb.write_to_sp(sti_file)
|
|
|
|
|
|
|
|
|
|
sti_file.write("VA[0] A[0] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
|
|
|
|
|
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
|
|
|
|
|
sti_file.write("VA[1] A[1] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
|
|
|
|
|
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
|
|
|
|
|
sti_file.write("VA[2] A[2] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
|
|
|
|
|
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
|
|
|
|
|
sti_file.write("VA[3] A[3] 0 PWL(0n {0} {1}n {0} {2}n 0 {3}n 0 {4}n {0})\n".format(tech.spice["supply_voltage"], 8.875 * tech.spice[
|
|
|
|
|
"clock_period"], 13.875 * tech.spice["clock_period"], 14.5 * tech.spice["clock_period"], 14.501 * tech.spice["clock_period"]))
|
|
|
|
|
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xA[0]_buff A[0] ADDR[0]_inv ADDR[0] vdd vss test_buf\n")
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xA[1]_buff A[1] ADDR[1]_inv ADDR[1] vdd vss test_buf\n")
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xA[2]_buff A[2] ADDR[2]_inv ADDR[2] vdd vss test_buf\n")
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xA[3]_buff A[3] ADDR[3]_inv ADDR[3] vdd vss test_buf\n")
|
|
|
|
|
|
|
|
|
|
VD_0 = sp_file.PWL(name="VD[0]", port=["D[0]", "0"])
|
|
|
|
|
VD_0.write_pulse(0, 5 * time_step, "S1")
|
|
|
|
|
VD_0.write_pulse(5 * time_step, 5 * time_step, "S0")
|
|
|
|
|
VD_0.write_to_sp(sti_file)
|
|
|
|
|
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xD[0]_buff D[0] DATA[0]_inv DATA[0]s vdd vss test_buf\n")
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xD[0]_gate DATA[0]s WEb WEb_inv DATA[0] vdd vss tran_gate\n")
|
|
|
|
|
sti_file.write("mp[0]_gate_vdd vdd write_v DATA[0] vdd " + str(tech.spice["pmos"]) +
|
|
|
|
|
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
sti_file.write("mn[0]_gate_vss vss write_g DATA[0] vss " + str(tech.spice["nmos"]) +
|
|
|
|
|
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
|
|
|
|
|
Vwrite_v = sp_file.PWL(name="write_v", port=["write_vs", "0"])
|
|
|
|
|
Vwrite_v.write_pulse(0, 0.5 * time_step, "S1")
|
|
|
|
|
Vwrite_v.write_pulse(7.5 * time_step, time_step, "DN")
|
|
|
|
|
Vwrite_v.write_to_sp(sti_file)
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xwrite_v write_vs write_v_inv write_v vdd vss test_buf\n")
|
|
|
|
|
|
|
|
|
|
Vwrite_g = sp_file.PWL(name="write_g", port=["write_gs", "0"])
|
|
|
|
|
Vwrite_g.write_pulse(0, 0.5 * time_step, "S0")
|
|
|
|
|
Vwrite_g.write_pulse(3 * time_step, time_step, "UP")
|
|
|
|
|
Vwrite_g.write_to_sp(sti_file)
|
|
|
|
|
sti_file.write(
|
|
|
|
|
"xwrite_g write_gs write_g_inv write_g vdd vss test_buf\n")
|
|
|
|
|
|
|
|
|
|
sti_file.write("Vdd vdd 0 DC " +
|
|
|
|
|
str(tech.spice["supply_voltage"]) + "\n")
|
|
|
|
|
sti_file.write("Vvss vss 0 DC 0\n")
|
|
|
|
|
sti_file.write(".ENDS source\n")
|
|
|
|
|
sti_file.write("\n")
|
|
|
|
|
|
|
|
|
|
sti_file.write(".SUBCKT tran_gate in gate gate_inv out vdd vss\n")
|
|
|
|
|
sti_file.write("mp0 in gate out vdd " + str(tech.spice["pmos"]) +
|
|
|
|
|
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
sti_file.write("mn0 in gate_inv out vss " + str(tech.spice["nmos"]) +
|
|
|
|
|
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
sti_file.write(".ENDS tran_gate\n")
|
|
|
|
|
sti_file.write("\n")
|
|
|
|
|
|
|
|
|
|
sti_file.write(".SUBCKT test_buf in out_inv out_buf vdd vss\n")
|
|
|
|
|
sti_file.write("mpinv1 out_inv in vdd vdd " + str(tech.spice["pmos"]) +
|
|
|
|
|
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
sti_file.write("mninv1 out_inv in vss vss " + str(tech.spice["nmos"]) +
|
|
|
|
|
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
sti_file.write("mpinv2 out_buf out_inv vdd vdd " + str(tech.spice["pmos"]) +
|
|
|
|
|
" w=" + str(2 * tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
sti_file.write("mninv2 out_buf out_inv vss vss " + str(tech.spice["nmos"]) +
|
|
|
|
|
" w=" + str(tech.parameter["min_tx_size"]) + "u" +
|
|
|
|
|
" l=" + str(tech.drc["minlength_channel"]) + "u" +
|
|
|
|
|
"\n")
|
|
|
|
|
sti_file.write(".ENDS test_buf\n")
|
|
|
|
|
sti_file.write("\n")
|
|
|
|
|
|
|
|
|
|
sti_file.file.close()
|
|
|
|
|
|
|
|
|
|
|
2018-11-03 00:34:26 +01:00
|
|
|
# run the test from the command line
|
2016-11-08 18:57:35 +01:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
(OPTS, args) = globals.parse_args()
|
|
|
|
|
del sys.argv[1:]
|
|
|
|
|
header(__file__, OPTS.tech_name)
|
2019-05-31 19:51:42 +02:00
|
|
|
unittest.main(testRunner=debugTestRunner())
|