from cyhdl import *
We create a little state machine with three states as shown below. Note: Upon reset, the @always_seq
logic implicitely resets state
variable to NEUTRAL
state.
@block
def hysteresis(
clk : ClkSignal,
reset : ResetSignal,
a : Signal,
q0 : Signal.Output,
q1 : Signal.Output,
*,
LOWER : int = 0,
UPPER : int = 255
):
t_state = enum('NEUTRAL', 'OVER', 'UNDER', name='t_state')
state = Signal(t_state.NEUTRAL)
@always_seq(clk.posedge, reset)
def worker():
if state == t_state.OVER:
if a < LOWER:
state.next = t_state.UNDER
elif state == t_state.UNDER:
if a > UPPER:
state.next = t_state.OVER
else:
if a < LOWER:
state.next = t_state.UNDER
elif a > UPPER:
state.next = t_state.OVER
@always_comb
def assign():
if state == t_state.OVER:
q0.next = False
q1.next = True
elif state == t_state.UNDER:
q0.next = True
q1.next = False
else:
q0.next = False
q1.next = False
return instances()
Then we create a test bench with a bit of ramping stimulus:
from myirl.test.common_test import run_ghdl, gen_osc
@block
def testbench_hyst(SIZE = 6):
clk = ClkSignal()
rst = ResetSignal(0, 1, isasync = False)
val = Signal(intbv()[SIZE:])
lo, hi = [ Signal(bool()) for i in range(2) ]
inst = hysteresis(clk = clk, reset = rst, a = val, q0 = lo, q1 = hi,
LOWER = 4, UPPER = 16)
cg = gen_osc(clk, CYCLE = 1)
N = 2 ** SIZE
@instance
def stim():
val.next = 8
rst.next = True
yield delay(10)
rst.next = False
for i in range(8, N):
val.next = i
yield delay(2)
for i in range(N-1, -1, -1):
val.next = i
yield delay(2)
val.next = 8
rst.next = True
yield delay(10)
rst.next = False
for i in range(8, -1, -1):
val.next = i
yield delay(5)
for i in range(0, N):
val.next = i
yield delay(2)
return instances()
def test():
tb = testbench_hyst()
files = tb.elab(targets.VHDL, elab_all = True)
run_ghdl(files, tb, vcdfile='hyst.vcd', debug=True)
return files
f = test()
Using default for SIZE: 6 SIZE: use default 6 Writing 'hysteresis' to file /tmp/myirl_testbench_hyst_lcsl0smp/hysteresis.vhdl Writing 'testbench_hyst' to file /tmp/myirl_testbench_hyst_lcsl0smp/testbench_hyst.vhdl Creating library file module_defs.vhdl WORK DIR of instance [Instance testbench_hyst I/F: [// ID: testbench_hyst_0 ]] /tmp/myirl_testbench_hyst_lcsl0smp/ ==== COSIM stdout ==== /tmp/testbench_hyst:info: simulation stopped by --stop-time @1us
As a result, the wave trace (download hyst.vcd) displays as follows in GTKwave:
!cat {f[1]}
-- File generated from source: -- /tmp/ipykernel_1348/4025283258.py -- (c) 2016-2022 section5.ch -- Modifications may be lost, edit the source file instead. library IEEE; use IEEE.std_logic_1164.all; use IEEE.numeric_std.all; library work; use work.txt_util.all; use work.myirl_conversion.all; entity testbench_hyst is end entity testbench_hyst; architecture cyriteHDL of testbench_hyst is -- Local type declarations -- Signal declarations signal lo : std_ulogic; signal hi : std_ulogic; signal clk : std_ulogic := '0'; signal rst : std_ulogic := '0'; signal val : unsigned(5 downto 0); begin -- Instance hysteresis inst_hysteresis_0: entity work.hysteresis generic map ( LOWER => 4, UPPER => 16 ) port map ( clk => clk, reset => rst, a => val, q0 => lo, q1 => hi ); CLKGEN_c17c: process(clk) begin clk <= not clk after 1.000000 ns; end process; stim: process variable i : integer; begin val <= "001000"; rst <= '1'; wait for 10 ns; rst <= '0'; for i in 8 to (64-(1)) loop val <= to_unsigned(i, 6); wait for 2 ns; end loop; for i in 63 to (-1-(-1)) loop val <= to_unsigned(i, 6); wait for 2 ns; end loop; val <= "001000"; rst <= '1'; wait for 10 ns; rst <= '0'; for i in 8 to (-1-(-1)) loop val <= to_unsigned(i, 6); wait for 5 ns; end loop; for i in 0 to (64-(1)) loop val <= to_unsigned(i, 6); wait for 2 ns; end loop; wait; end process; end architecture cyriteHDL;