from myirl.emulation.myhdl 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()
from myirl import targets
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()
FALLBACK: UNHANDLED ROOT CLASS <class 'ipykernel.zmqshell.ZMQInteractiveShell'>, create new context Using default for SIZE: 6 SIZE: use default 6 Writing 'hysteresis' to file /tmp/myirl_testbench_hyst_6t78zkvg/hysteresis.vhdl Writing 'testbench_hyst' to file /tmp/myirl_testbench_hyst_6t78zkvg/testbench_hyst.vhdl Creating library file /tmp/myirl_module_defs_f_k2slc6/module_defs.vhdl ==== 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[0]}
-- File generated from source: -- /tmp/ipykernel_30046/1616138709.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 hysteresis is generic ( LOWER: natural := 0; UPPER: natural := 255 ); port ( clk : in std_ulogic; reset : in std_ulogic; a : in unsigned(5 downto 0); q0 : out std_ulogic; q1 : out std_ulogic ); end entity hysteresis; architecture myhdl_emulation of hysteresis is -- Local type declarations type t_state is ( t_state_NEUTRAL, t_state_OVER, t_state_UNDER ); -- Signal declarations signal state : t_state; begin worker: process(clk, reset) begin if rising_edge(clk) then if reset = '1' then state <= t_state_NEUTRAL; else if (state = t_state_OVER) then if (LOWER > a) then state <= t_state_UNDER; end if; elsif (state = t_state_UNDER) then if (a > UPPER) then state <= t_state_OVER; end if; elsif (LOWER > a) then state <= t_state_UNDER; elsif (a > UPPER) then state <= t_state_OVER; end if; end if; end if; end process; assign: process(state) begin if (state = t_state_OVER) then q0 <= '0'; q1 <= '1'; elsif (state = t_state_UNDER) then q0 <= '1'; q1 <= '0'; else q0 <= '0'; q1 <= '0'; end if; end process; end architecture myhdl_emulation;