I have been working with MyHDL for a few months and I find it really great. Thanks for bringing joy of hardware programing back
Today I’ve revisited an issue I gave up in the past and I still can’t convince myself there isn’t a nice way of doing.
I have a protocol implemented in FPGA where I use MyHDL for the full development cycle, simulation, conversion to VHDL and then syntesis to Lattice device. Within this protocol I have a simple “command” field, for which I used enum so I can see pretty names on simulation. Example:
where cmd.next is expect to be like t_CMD.NONE (EnumItem type).
The problem lies in the “getCmd()” implementation… I can’t think of a good and syntetizable way of geting EnumItem back from integer code that is not an ugly sequence of if’s like:
def getCmd(val):
if val == CMD_NONE:
return t_CMD.NONE
elif val == CMD_RD:
return t_CMD.RD
(...)
I have tried a few ideas, like iterating through enum reftype() and then using getattr(t_CMD, name) or preloading all t_CMD values to a list, but they all failed to convert. (although some work on simulation)
Am I missing some clever way of doing this lookup / type conversion?
Hi @josyb ! Thanks for replying. What I was trying to achieve specifically is a synthesizable solution. I did manage to implement alternatives that work on test-bench only, but they fail to synthesize.
And the only synthesizable solution I’ve got so far is that ugly sequence of if’s checking every possible value…
'''
Created on 14 apr. 2025
@author: josy
'''
from myhdl import block, Signal, intbv, enum, always_seq, instances, Constant
t_CMD = enum("NOP", "RD", "WR", "ID", "PROBE", "ADDR", encoding="binary")
@block
def tryenum(Clk, D, Q):
choices = [Constant(t_CMD.__dict__[t_CMD._names[i]]) for i in range(t_CMD._nritems)]
@always_seq(Clk.posedge, reset=None)
def synch():
if D == 0:
Q.next = t_CMD.NOP
elif D == 1:
Q.next = t_CMD.RD
else:
Q.next = choices[D]
return instances()
if __name__ == '__main__':
Clk = Signal(bool(0))
D = Signal(intbv(0)[3:])
Q = Signal(t_CMD.NOP)
dfc = tryenum(Clk, D, Q)
dfc.convert(hdl='VHDL')
and this gives this result:
-- File: tryenum.vhd
-- Generated by MyHDL 0.11.51
-- Date: Mon Apr 14 19:25:06 2025 UTC
package pck_tryenum is
attribute enum_encoding : string;
type t_enum_t_CMD_1 is (
NOP,
RD,
WR,
ID,
PROBE,
ADDR
);
attribute enum_encoding of t_enum_t_CMD_1 : type is "000 001 010 011 100 101";
end package pck_tryenum;
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
use std.textio.all;
use work.pck_myhdl_011.all;
use work.pck_tryenum.all;
entity tryenum is
port(
Clk : in std_logic;
D : in unsigned(2 downto 0);
Q : out t_enum_t_CMD_1
);
end entity tryenum;
architecture MyHDL of tryenum is
type t_array_choices is array (0 to 6 - 1) of t_enum_t_CMD_1;
constant choices : t_array_choices := (
NOP,
RD,
WR,
ID,
PROBE,
ADDR);
begin
synch : process(Clk) is
begin
if rising_edge(Clk) then
case D is
when "000" =>
Q <= NOP;
when "001" =>
Q <= RD;
when others =>
Q <= choices(to_integer(D));
end case;
end if;
end process synch;
end architecture MyHDL;
Will this work for you?
We will have to think on how to improve on t_CMD.__dict__[t_CMD._names[i]] because that is certainly not beautiful at all
I’ve tried a couple variations based on your suggestion, but couldn’t get any to work:
def getCmd3(val):
return Constant(t_CMD.__dict__[t_CMD._names[val]])
Can't infer return type
def getCmd3(val):
return t_CMD.__dict__[t_CMD._names[val]]
AttributeError: 'dict' object has no attribute '_toVHDL'
def getCmd3(val):
for i in range(t_CMD._nritems):
if i == val:
return Constant(t_CMD.__dict__[t_CMD._names[i]])
return t_CMD.NONE
Can't infer return type
def getCmd3(val):
for i in range(t_CMD._nritems):
if i == val:
return t_CMD.__dict__[t_CMD._names[i]]
return t_CMD.NONE
Return type mismatch
def getCmd3(val):
choices = [Constant(t_CMD.__dict__[t_CMD._names[i]]) for i in range(t_CMD._nritems)]
return choices[val]
Unsupported list comprehension form: should be [intbv()[n:] for i in range(m)]