for release 0.0.5

* Techlib: fix NanGate variants
* Circuit: more visitor and manipulator utilities
* mv_transition: produce stable values when arguments are UNASSIGNED
* Verilog: better assign processing
* STIL parsing improvements
* SDF: conditional parsing (semantics ignored for now), better interconnect annotation, more robust name matching
* sim: use ALAP topo-sort
* logic_sim: improved fault injection, faster 6V sim
* wave_sim: use correct thresholds for pulse filtering, partial delta-sim support, per-sim delay factor
* log: fix limiter
This commit is contained in:
stefan committed 2025-06-23 15:39:55 +09:00
1 parent 9f9902c613
commit aea633ac8d
22 files changed
+999 -380

No files matched your search

Binary file not shown.
+36
View File
@@ -13,8 +13,44 @@ def b15_2ig_circuit(mydir):
from kyupy.techlib import SAED32
return verilog.load(mydir / 'b15_2ig.v.gz', branchforks=True, tlib=SAED32)
@pytest.fixture(scope='session')
def b15_2ig_circuit_resolved(b15_2ig_circuit):
from kyupy.techlib import SAED32
cr = b15_2ig_circuit.copy()
cr.resolve_tlib_cells(SAED32)
return cr
@pytest.fixture(scope='session')
def b15_4ig_circuit(mydir):
from kyupy import verilog
from kyupy.techlib import SAED32
return verilog.load(mydir / 'b15_4ig.v.gz', branchforks=True, tlib=SAED32)
@pytest.fixture(scope='session')
def b15_4ig_circuit_resolved(b15_4ig_circuit):
from kyupy.techlib import SAED32
cr = b15_4ig_circuit.copy()
cr.resolve_tlib_cells(SAED32)
return cr
@pytest.fixture(scope='session')
def b15_2ig_delays(mydir, b15_2ig_circuit):
from kyupy import sdf
from kyupy.techlib import SAED32
return sdf.load(mydir / 'b15_2ig.sdf.gz').iopaths(b15_2ig_circuit, tlib=SAED32)[1:2]
@pytest.fixture(scope='session')
def b15_2ig_sa_nf_test_resp(mydir, b15_2ig_circuit_resolved):
from kyupy import stil
s = stil.load(mydir / 'b15_2ig.sa_nf.stil.gz')
tests = s.tests(b15_2ig_circuit_resolved)[:,1:]
resp = s.responses(b15_2ig_circuit_resolved)[:,1:]
return (tests, resp)
@pytest.fixture(scope='session')
def b15_4ig_sa_rf_test_resp(mydir, b15_4ig_circuit_resolved):
from kyupy import stil
s = stil.load(mydir / 'b15_4ig.sa_rf.stil.gz')
tests = s.tests(b15_4ig_circuit_resolved)[:,1:]
resp = s.responses(b15_4ig_circuit_resolved)[:,1:]
return (tests, resp)
+33 -6
View File
@@ -7,22 +7,49 @@
(TEMPERATURE 25.00:25.00:25.00)
(TIMESCALE 1ns)
(CELL
(CELLTYPE "NAND2X1")
(CELLTYPE "NAND2_X1")
(INSTANCE nandgate)
(DELAY
(ABSOLUTE
(IOPATH IN1 QN (0.099:0.103:0.103) (0.122:0.127:0.127))
(IOPATH IN2 QN (0.083:0.086:0.086) (0.100:0.104:0.104))
(IOPATH A1 ZN (0.099:0.103:0.103) (0.122:0.127:0.127))
(IOPATH A2 ZN (0.083:0.086:0.086) (0.100:0.104:0.104))
)
)
)
(CELL
(CELLTYPE "AND2X1")
(CELLTYPE "AND2_X1")
(INSTANCE andgate)
(DELAY
(ABSOLUTE
(IOPATH IN1 Q (0.367:0.378:0.378) (0.351:0.377:0.377))
(IOPATH IN2 Q (0.366:0.375:0.375) (0.359:0.370:0.370))
(IOPATH A1 ZN (0.367:0.378:0.378) (0.351:0.377:0.377))
(IOPATH A2 ZN (0.366:0.375:0.375) (0.359:0.370:0.370))
)
)
)
(CELL
(CELLTYPE "OAI21_X1")
(INSTANCE oai21gate)
(DELAY
(ABSOLUTE
(IOPATH B1 ZN (0.000:0.025:0.025) (0.000:0.013:0.013))
(IOPATH B2 ZN (0.000:0.030:0.030) (0.000:0.016:0.016))
( COND (B1 == 1'b0) && (B2 == 1'b1) (IOPATH A ZN (0.000:0.018:0.018)))
(COND (B1 == 1'b1) && (B2 == 1'b0) (IOPATH A ZN (0.000:0.018:0.018) (0.000:0.016:0.016)))
(COND (B1 == 1'b1) && (B2 == 1'b1) (IOPATH A ZN (0.000:0.019:0.019) (0.000:0.014:0.014)))
)
)
)
(CELL
(CELLTYPE "MUX2_X1")
(INSTANCE mux2gate)
(DELAY
(ABSOLUTE
(COND (B == 1'b0) && (S == 1'b0) (IOPATH A Z (0.000:0.037:0.037) (0.000:0.058:0.058)))
(COND (B == 1'b1) && (S == 1'b0) (IOPATH A Z (0.000:0.037:0.037) (0.000:0.058:0.058)))
(COND (A == 1'b0) && (S == 1'b1) (IOPATH B Z (0.000:0.035:0.035) (0.000:0.056:0.056)))
(COND (A == 1'b1) && (S == 1'b1) (IOPATH B Z (0.000:0.035:0.035) (0.000:0.056:0.056)))
(COND (A == 1'b0) && (B == 1'b1) (IOPATH S Z (0.000:0.047:0.047) (0.000:0.073:0.073)))
(COND (A == 1'b1) && (B == 1'b0) (IOPATH S Z (0.000:0.072:0.072) (0.000:0.064:0.064)))
)
)
)
+8 -4
View File
@@ -1,11 +1,15 @@
module gates (a, b, o0, o1 );
module gates (a, b, c, o0, o1, o2, o3 );
input a;
input b;
input c;
output o0;
output o1;
output o2;
output o3;
AND2X1 andgate (.IN1 ( a ) , .IN2 ( b ) , .Q ( o0 ) ) ;
NAND2X1 nandgate (.IN1 ( a ) , .IN2 ( b ) , .QN ( o1 ) ) ;
AND2_X1 andgate (.A1 ( a ) , .A2 ( b ) , .ZN ( o0 ) ) ;
NAND2_X1 nandgate (.A1 ( a ) , .A2 ( b ) , .ZN ( o1 ) ) ;
OAI21_X1 oai21gate (.B1(a), .B2(b), .A(c), .ZN(o2) ) ;
MUX2_X1 mux2gate (.A(a), .B(b), .S(c), .Z(o3)) ;
endmodule
+22 -1
View File
@@ -1,9 +1,30 @@
import pickle
from kyupy.circuit import Circuit, Node, Line
from kyupy.circuit import GrowingList, Circuit, Node, Line
from kyupy import verilog, bench
from kyupy.techlib import SAED32
def test_growing_list():
gl = GrowingList()
assert gl.free_idx == 0
gl[0] = 1
assert gl.free_idx == 1
gl[2] = 1
assert gl.free_idx == 1
gl[0] = None
assert gl.free_idx == 0
gl[0] = 1
assert gl.free_idx == 1
gl[1] = 1
assert gl.free_idx == 3
gl.append(1)
assert gl.free_idx == 4
gl[2] = None
assert gl.free_idx == 2
gl[2] = 1
gl[1] = None
assert gl.free_idx == 1
def test_lines():
c = Circuit()
n1 = Node(c, 'n1')
+86 -1
View File
@@ -1,6 +1,6 @@
import numpy as np
from kyupy.logic_sim import LogicSim
from kyupy.logic_sim import LogicSim, LogicSim6V
from kyupy import bench, logic, sim
from kyupy.logic import mvarray, bparray, bp_to_mv, mv_to_bp
@@ -94,6 +94,30 @@ def test_4v():
'--0XX', '--X1X', '--XXX', '--XXX'))
def test_6v():
c = bench.parse('input(x, y) output(a, o, n, xo, no) a=AND2(x,y) o=OR2(x,y) n=INV1(x) xo=XOR2(x,y) no=NOR2(x,y)')
s = LogicSim6V(c, 36)
assert s.s_len == 7
mva = mvarray(
'0000101', '0101110', '0R0R1RF', '0F0F1FR', '0P0P1PN', '0N0N1NP',
'1001010', '1111000', '1RR10F0', '1FF10R0', '1PP10N0', '1NN10P0',
'R00RFRF', 'R1R1FF0', 'RRRRFPF', 'RFPNFNP', 'RPPRFRF', 'RNRNFFP',
'F00FRFR', 'F1F1RR0', 'FRPNRNP', 'FFFFRPR', 'FPPFRFR', 'FNFNRRP',
'P00PNPN', 'P1P1NN0', 'PRPRNRF', 'PFPFNFR', 'PPPPNPN', 'PNPNNNP',
'N00NPNP', 'N1N1PP0', 'NRRNPFP', 'NFFNPRP', 'NPPNPNP', 'NNNNPPP')
tests = np.copy(mva)
tests[2:] = logic.ZERO
s.s[0] = tests
s.s_to_c()
s.c_prop()
s.c_to_s()
resp = s.s[1].copy()
exp_resp = np.copy(mva)
exp_resp[:2] = logic.ZERO
np.testing.assert_allclose(resp, exp_resp)
def test_8v():
c = bench.parse('input(x, y) output(a, o, n, xo) a=and(x,y) o=or(x,y) n=not(x) xo=xor(x,y)')
s = LogicSim(c, 64, m=8)
@@ -173,3 +197,64 @@ def test_b01(mydir):
s.c_prop()
s.c_to_s()
bp_to_mv(s.s[1])
def sim_and_compare(c, test_resp, m=8):
tests, resp = test_resp
lsim = LogicSim(c, m=m, sims=tests.shape[1])
lsim.s[0] = logic.mv_to_bp(tests)
lsim.s_to_c()
lsim.c_prop()
lsim.c_to_s()
resp_sim = logic.bp_to_mv(lsim.s[1])[:,:tests.shape[1]]
idxs, pats = np.nonzero(((resp == logic.ONE) & (resp_sim != logic.ONE)) | ((resp == logic.ZERO) & (resp_sim != logic.ZERO)))
for i, (idx, pat) in enumerate(zip(idxs, pats)):
if i >= 10:
print(f'...')
break
print(f'mismatch pattern:{pat} ppio:{idx} exp:{logic.mv_str(resp[idx,pat])} act:{logic.mv_str(resp_sim[idx,pat])}')
assert len(idxs) == 0
def sim_and_compare_6v(c, test_resp):
tests, resp = test_resp
lsim = LogicSim6V(c, sims=tests.shape[1])
lsim.s[0] = tests
lsim.s_to_c()
lsim.c_prop()
lsim.c_to_s()
resp_sim = lsim.s[1]
idxs, pats = np.nonzero(((resp == logic.ONE) & (resp_sim != logic.ONE)) | ((resp == logic.ZERO) & (resp_sim != logic.ZERO)))
for i, (idx, pat) in enumerate(zip(idxs, pats)):
if i >= 10:
print(f'...')
break
print(f'mismatch pattern:{pat} ppio:{idx} exp:{logic.mv_str(resp[idx,pat])} act:{logic.mv_str(resp_sim[idx,pat])}')
assert len(idxs) == 0
def test_b15_2ig_sa_2v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=2)
def test_b15_2ig_sa_4v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=4)
def test_b15_2ig_sa_6v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare_6v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp)
def test_b15_2ig_sa_8v(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp):
sim_and_compare(b15_2ig_circuit_resolved, b15_2ig_sa_nf_test_resp, m=8)
def test_b15_4ig_sa_2v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=2)
def test_b15_4ig_sa_4v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=4)
def test_b15_4ig_sa_8v(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp):
sim_and_compare(b15_4ig_circuit_resolved, b15_4ig_sa_rf_test_resp, m=8)
+3 -3
View File
@@ -2,7 +2,7 @@ import numpy as np
from kyupy import sdf, verilog, bench
from kyupy.wave_sim import WaveSim, TMAX, TMIN
from kyupy.techlib import SAED32, SAED90
from kyupy.techlib import SAED32, NANGATE45
def test_parse():
test = '''
@@ -80,9 +80,9 @@ def test_b15(mydir):
def test_gates(mydir):
c = verilog.load(mydir / 'gates.v', tlib=SAED90)
c = verilog.load(mydir / 'gates.v', tlib=NANGATE45)
df = sdf.load(mydir / 'gates.sdf')
lt = df.iopaths(c, tlib=SAED90)[1]
lt = df.iopaths(c, tlib=NANGATE45)[1]
nand_a = c.cells['nandgate'].ins[0]
nand_b = c.cells['nandgate'].ins[1]
and_a = c.cells['andgate'].ins[0]
+6 -6
View File
@@ -1,5 +1,5 @@
from kyupy import verilog
from kyupy.techlib import SAED90, SAED32
from kyupy.techlib import SAED90, SAED32, NANGATE45
def test_b01(mydir):
with open(mydir / 'b01.v', 'r') as f:
@@ -26,12 +26,12 @@ def test_b15(mydir):
def test_gates(mydir):
c = verilog.load(mydir / 'gates.v', tlib=SAED90)
assert len(c.nodes) == 10
assert len(c.lines) == 10
c = verilog.load(mydir / 'gates.v', tlib=NANGATE45)
assert len(c.nodes) == 18
assert len(c.lines) == 21
stats = c.stats
assert stats['input'] == 2
assert stats['output'] == 2
assert stats['input'] == 3
assert stats['output'] == 4
assert stats['__seq__'] == 0
+47 -13
View File
@@ -5,22 +5,56 @@ from kyupy.logic_sim import LogicSim
from kyupy import logic, bench, sim
from kyupy.logic import mvarray
def test_xnor2_delays():
op = (sim.XNOR2, 2, 0, 1, 3, 3, -1, 0, 0)
#op = (0b0111, 4, 0, 1)
c = np.full((4*16, 1), TMAX, dtype=np.float32) # 4 waveforms of capacity 16
c_locs = np.zeros((4,), dtype='int')
c_caps = np.zeros((4,), dtype='int')
ebuf = np.zeros((4, 1, 2), dtype=np.int32)
for i in range(4): c_locs[i], c_caps[i] = i*16, 16 # 1:1 mapping
delays = np.zeros((1, 4, 2, 2))
delays[0, 0, 0, 0] = 0.031 # A rise -> Z rise
delays[0, 0, 0, 1] = 0.027 # A rise -> Z fall
delays[0, 0, 1, 0] = 0.033 # A fall -> Z rise
delays[0, 0, 1, 1] = 0.037 # A fall -> Z fall
delays[0, 1, 0, 0] = 0.032 # B rise -> Z rise
delays[0, 1, 0, 1] = 0.030 # B rise -> Z fall
delays[0, 1, 1, 0] = 0.038 # B fall -> Z rise
delays[0, 1, 1, 1] = 0.036 # B fall -> Z fall
simctl_int = np.asarray([0], dtype=np.int32)
def wave_assert(inputs, output):
for i, a in zip(inputs, c.reshape(-1,16)): a[:len(i)] = i
wave_eval_cpu(op, c, c_locs, c_caps, ebuf, 0, delays, simctl_int, 0, 0)
for i, v in enumerate(output): np.testing.assert_allclose(c.reshape(-1,16)[2,i], v)
wave_assert([[TMIN,TMAX],[TMIN,TMAX]], [TMIN,TMAX]) # XNOR(1,1) => 1
wave_assert([[TMAX,TMAX],[TMIN,TMAX]], [TMAX]) # XNOR(0,1) => 0
# using Afall/Zfall for pulse length, bug: was using Arise/Zfall
#wave_assert([[0.07, 0.10, TMAX], [0.0, TMAX]], [TMIN, 0.03, 0.101, 0.137, TMAX])
wave_assert([[0.07, 0.10, TMAX], [0.0, TMAX]], [TMIN, 0.03, TMAX])
wave_assert([[0.06, 0.10, TMAX], [0.0, TMAX]], [TMIN, 0.03, 0.091, 0.137, TMAX])
def test_nand_delays():
op = (sim.NAND4, 4, 0, 1, 2, 3, -1, 0, 0)
#op = (0b0111, 4, 0, 1)
c = np.full((5*16, 1), TMAX) # 5 waveforms of capacity 16
c = np.full((5*16, 1), TMAX, dtype=np.float32) # 5 waveforms of capacity 16
c_locs = np.zeros((5,), dtype='int')
c_caps = np.zeros((5,), dtype='int')
ebuf = np.zeros((4, 1, 2), dtype=np.int32)
for i in range(5): c_locs[i], c_caps[i] = i*16, 16 # 1:1 mapping
# SDF specifies IOPATH delays with respect to output polarity
# SDF pulse rejection value is determined by IOPATH causing last transition and polarity of last transition
delays = np.zeros((1, 5, 2, 2))
delays[0, 0, 0, 0] = 0.1 # A -> Z rise delay
delays[0, 0, 0, 1] = 0.2 # A -> Z fall delay
delays[0, 0, 1, 0] = 0.1 # A -> Z negative pulse limit (terminate in rising Z)
delays[0, 0, 1, 1] = 0.2 # A -> Z positive pulse limit
delays[0, 0, 0, 0] = 0.1 # A rise -> Z rise
delays[0, 0, 0, 1] = 0.2 # A rise -> Z fall
delays[0, 0, 1, 0] = 0.1 # A fall -> Z rise
delays[0, 0, 1, 1] = 0.2 # A fall -> Z fall
delays[0, 1, :, 0] = 0.3 # as above for B -> Z
delays[0, 1, :, 1] = 0.4
delays[0, 2, :, 0] = 0.5 # as above for C -> Z
@@ -32,7 +66,7 @@ def test_nand_delays():
def wave_assert(inputs, output):
for i, a in zip(inputs, c.reshape(-1,16)): a[:len(i)] = i
wave_eval_cpu(op, c, c_locs, c_caps, 0, delays, simctl_int)
wave_eval_cpu(op, c, c_locs, c_caps, ebuf, 0, delays, simctl_int, 0, 0)
for i, v in enumerate(output): np.testing.assert_allclose(c.reshape(-1,16)[4,i], v)
wave_assert([[TMAX,TMAX],[TMAX,TMAX],[TMIN,TMAX],[TMIN,TMAX]], [TMIN,TMAX]) # NAND(0,0,1,1) => 1
@@ -145,7 +179,7 @@ def compare_to_logic_sim(wsim: WaveSim):
lsim.s_to_c()
lsim.c_prop()
lsim.c_to_s()
exp = logic.bp_to_mv(lsim.s[1])
exp = logic.bp_to_mv(lsim.s[1])[:,:tests.shape[-1]]
resp[resp == logic.PPULSE] = logic.ZERO
resp[resp == logic.NPULSE] = logic.ONE
@@ -156,13 +190,13 @@ def compare_to_logic_sim(wsim: WaveSim):
np.testing.assert_allclose(resp, exp)
def test_b15(b15_2ig_circuit, b15_2ig_delays):
compare_to_logic_sim(WaveSim(b15_2ig_circuit, b15_2ig_delays, 8))
def test_b15(b15_2ig_circuit_resolved, b15_2ig_delays):
compare_to_logic_sim(WaveSim(b15_2ig_circuit_resolved, b15_2ig_delays, 8))
def test_b15_strip_forks(b15_2ig_circuit, b15_2ig_delays):
compare_to_logic_sim(WaveSim(b15_2ig_circuit, b15_2ig_delays, 8, strip_forks=True))
def test_b15_strip_forks(b15_2ig_circuit_resolved, b15_2ig_delays):
compare_to_logic_sim(WaveSim(b15_2ig_circuit_resolved, b15_2ig_delays, 8, strip_forks=True))
def test_b15_cuda(b15_2ig_circuit, b15_2ig_delays):
compare_to_logic_sim(WaveSimCuda(b15_2ig_circuit, b15_2ig_delays, 8, strip_forks=True))
def test_b15_cuda(b15_2ig_circuit_resolved, b15_2ig_delays):
compare_to_logic_sim(WaveSimCuda(b15_2ig_circuit_resolved, b15_2ig_delays, 8, strip_forks=True))