Example: Hysteresis with simple FSM

In [1]:
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.

In [2]:
@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:

In [3]:
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()
In [4]:
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
In [5]:
f = test()
Using default for SIZE: 6
 SIZE: use default 6 
Creating process 'hysteresis/worker' with sensitivity (clk'rising, <reset>)
Creating sequential 'testbench_hyst/stim' 
 Elaborating component hysteresis_s1_s1_s6_s1_s1_4_16 
 Writing 'hysteresis' to file /tmp/myirl_top_testbench_hyst_fu_w7x72/hysteresis.vhdl 
 Elaborating component testbench_hyst_6 
 Writing 'testbench_hyst' to file /tmp/myirl_top_testbench_hyst_fu_w7x72/testbench_hyst.vhdl 
 Creating library file /tmp/myirl_module_defs_xeeh4rwe/module_defs.vhdl 
==== COSIM stdout ====

==== COSIM stderr ====

==== COSIM stdout ====
analyze /home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/../test/vhdl/txt_util.vhdl
analyze /home/testing/.local/lib/python3.9/site-packages/myirl-0.0.0-py3.9-linux-x86_64.egg/myirl/targets/libmyirl.vhdl
analyze /tmp/myirl_top_testbench_hyst_fu_w7x72/hysteresis.vhdl
analyze /tmp/myirl_top_testbench_hyst_fu_w7x72/testbench_hyst.vhdl
elaborate testbench_hyst

==== COSIM stderr ====

==== COSIM stdout ====
/tmp/testbench_hyst:info: simulation stopped by --stop-time @1us

==== COSIM stderr ====

As a result, the wave trace (download hyst.vcd) displays as follows in GTKwave:

Wave trace

In [6]:
!cat {f[0]}
-- File generated from source:
--     /tmp/ipykernel_10240/1616138709.py
-- (c) 2016-2021 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 MyIRL 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 MyIRL;

In [ ]: