Instantiating FPGA components

Hi,

I’m experimenting for a couple of days now with MyHDL but cannot find a way to instantiate specific/dedicated, for Xilinx, components as MMCM, PLL, URAM, …

  • Is it at all possible to instantiate those components?
  • If yes:
    • How can this be done?
    • How are those components be hooked up in the Python design/code?
      • Simply use the signal names?
  • If no:
    • MyHDL can thus only be used to convert to regular/standard VHDL?
    • If specific FPGA features as MMCM and other components are needed a VHDL description is still necessary. That VHDL description can then contain MyHDL converted modules. Correct?

Thanks,

We do this quite a bit. For ip blocks (the ones you select in the block diagram), you can use Ovenbird. For lower level primitives, it’s a bit more subtle to get right (though Ovenbird could be extended to do it without too much work).

The problem is how to make it work seamlessly and for arbitrary number of primitives. For a one off, it’s not too hard. And it’s easier in Verilog then VHDL.

1 Like

hi,

I’m using iCE40 parts, and also want seamless transition from sim to synth. I pass ‘SynType’ as an optional parameter through the design to select simulation or conversion for use in library parts etc.

I do not like the extra level of nesting, but have not recently found a way to work without it. Having some working code is good for now.

"""    SB_RGBA_DRV.py      """

from myhdl import * 

@block
def SB_RGBA_DRV_Sim(   \
    RGBLEDEN,
    RGB0PWM, RGB1PWM, RGB2PWM, 
    CURREN, 
    RGB0, RGB1, RGB2, 
    ): 

    @always_comb
    def PWMsimLogic():
        RGB0.next = RGB0PWM
        RGB1.next = RGB1PWM
        RGB2.next = RGB2PWM

    return PWMsimLogic

@block
def SB_RGBA_DRV_Inst(   \
    RGBLEDEN,
    RGB0PWM, RGB1PWM, RGB2PWM, 
    CURREN, 
    RGB0, RGB1, RGB2, 
    ): 
    ''' Instantiation of library part SB_RGBA_DRV '''
    
    RGB0_CURRENT = "0b000001"
    RGB1_CURRENT = "0b000001"
    RGB2_CURRENT = "0b000001"
    
    @always()
    def logic():
        pass

    RGB0.driven = "wire"  
    RGB1.driven = "wire"  
    RGB2.driven = "wire"  
    RGB0PWM.read = True
    RGB1PWM.read = True
    RGB2PWM.read = True

    return logic
    
SB_RGBA_DRV_Inst.verilog_code =   \
"""
// Instantiation of library part 
SB_RGBA_DRV SB_RGBA_DRV_01 ( 
  .RGBLEDEN (1'b1),
  .RGB0PWM  ($RGB0PWM),  // green
  .RGB1PWM  ($RGB1PWM),  // blue
  .RGB2PWM  ($RGB2PWM),  // red
  .CURREN   (1'b1), 
  .RGB0     (GRNn),		// green on UPDuino v2.0 Board
  .RGB1     (BLUn),     // blue
  .RGB2     (REDn)      // red  
);
defparam SB_RGBA_DRV_01.RGB0_CURRENT = "$RGB0_CURRENT";  // "0b000001";
defparam SB_RGBA_DRV_01.RGB1_CURRENT = "$RGB1_CURRENT";
defparam SB_RGBA_DRV_01.RGB2_CURRENT = "$RGB2_CURRENT";
"""
  
SB_RGBA_DRV_Inst.VHDL_code = """  \
-- Instantiation of library part 
SB_RGBA_DRV: SB_RGBA_DRV_01
--generic map(
--    RGB0_CURRENT => "$RGB0_CURRENT";  -- "0b000001",
--    RGB1_CURRENT => "$RGB0_CURRENT",
--    RGB2_CURRENT => "$RGB0_CURRENT";
port map( 
   RGBLEDEN -> (1'b1),
   RGB0PWM  -> ($RGB0PWM),  -- green
   RGB1PWM  -> ($RGB1PWM),  -- blue
   RGB2PWM  -> ($RGB2PWM),  -- red
   CURREN   -> (1'b1), 
   RGB0     -> (GRNn),   -- green on UPDuino v2.0 Board
   RGB1     -> (BLUn),    -- blue
   RGB2     -> (REDn)     -- red  
);
"""
  

@block
def SB_RGBA_DRV(   \
    RGBLEDEN,
    RGB0PWM, RGB1PWM, RGB2PWM, 
    CURREN, 
    RGB0, RGB1, RGB2, 
    SynType ): 
    ''' SiBlu library part SB_RGBA_DRV '''

    if SynType=="Sim":  mySB_RGBA_DRV = SB_RGBA_DRV_Sim
    else:               mySB_RGBA_DRV = SB_RGBA_DRV_Inst
    RGBdrv01 = mySB_RGBA_DRV( \
        1, RGB0PWM, RGB1PWM, RGB2PWM, 
        1, RGB0, RGB1, RGB2,  )

    return RGBdrv01

This is working, but incomplete: the LED is fixed at minimum drive current. Also, the project VHDL export is not working.

This uninitialized RAM model works without extra symbolic indirection, and the demo project exports Verilog and VHDL. (No hardware test yet)

"""    SB_RAM256X16_2013-08-28.py

2018-11-02 jc changed "(% s)" substitution to "$" type
"""

from myhdl import * 
from random import randint


RdLatency = 3

@block
def SB_RAM256X16(RDATA, RADDR, RCLK, RCLKE, RE,    \
                        WADDR, WCLK, WCLKE, WDATA, WE, MASK, SynType ): 
    ''' sim & synth version of iCE40 block RAM '''
  
    mem = [ Signal(intbv(0x5555)[16:]) for ii in range(256)]
    
    RData_RClk = Signal(intbv(0)[16:])

    
    @always(RCLK.posedge)
    def RData_RClkLogic():
        if ((RCLKE==1) and (RE==1)):
            RData_RClk.next = mem[int(RADDR)]

    @always(delay(RdLatency))
    def RDATAlogic():
        RDATA.next = RData_RClk

    WData_WClk = Signal(intbv(0)[16:])
    WAddr_WClk = Signal(intbv(0)[8:])
    WEtm1, WEtm2 = [Signal(bool(0)) for ii in range(2)]
    
    @always(WCLK.posedge)
    def WEtm1Logic():
        ''' WEtm1 WEtm2 write process timing '''
        WEtm1.next = WE & WCLKE
        WEtm2.next = WEtm1 
    
    @always(WCLK.posedge)
    def WData_WClkLogic():
        if WCLKE & WEtm1:
            WData_WClk.next = WDATA
            WAddr_WClk.next = WADDR
    
    @always(WCLK.posedge)
    def WriteLogic():
        if WEtm1 & WEtm2 & WCLKE & WE:
            mem[int(WADDR)].next =  \
                    (~MASK & WDATA) | (MASK & mem[WADDR])
            #mem[int(WADDR)].next = WDATA

    @always()
    def NoLogic():
        ''' empty logic '''
        pass
  
    ''' connection assurance '''
    RDATA.driven = "wire"  
    RADDR.read = True
    RCLK.read = True
    RCLKE.read = True
    RE.read = True
    WADDR.read = True
    WCLK.read = True
    WCLKE.read = True
    WDATA.read = True
    WE.read = True
    MASK.read = True  # may cause trouble?
    
    SB_RAM256X16.verilog_code =  \
"""// iCE40 Library Part \\\\
SB_RAM256x16 SB_RAM256X16inst(
    .RDATA($RDATA), 
    .RADDR($RADDR), 
    .RCLK($RCLK), 
    .RCLKE($RCLKE), 
    .RE($RE), 
    .WADDR($WADDR), 
    .WCLK($WCLK), 
    .WCLKE($WCLKE), 
    .WDATA($WDATA), 
    .WE($WE), 
    .MASK($MASK) 
);
"""

    SB_RAM256X16.vhdl_code =  \
"""-- iCE40 Library Part --
RAM256X16: SB_RAM256X16
    port map(
      RDATA => $RDATA,
      RADDR => $RADDR,
      RCLK => $RCLK,
      RCLKE => $RCLKE,
      RE => $RE,
      WADDR => $WADDR,
      WCLK=> $WCLK,
      WCLKE => $WCLKE,
      WDATA => $WDATA,
      MASK => $MASK,
      WE => $WE  
);
"""
    
    if SynType=='Sim':
        return  RData_RClkLogic, RDATAlogic, WEtm1Logic, \
        WData_WClkLogic, WriteLogic   
    else: 
        return NoLogic

There are other library parts which are working but incomplete, including an initialized RAM.

Jan Coombs

Ok,

Thus without extras, puzzling things together with other tools and/or additions it seems not possible to go from MyHDL, via intermediary VHDL or verilog, to a direct implementable design.

The way to go, seems to, is to create a hierarchical design were some of the hierarchy is written and simulated in python, translated to a form of HDL. Then that VHDL/verilog is assembled with VHDL/verilog sources dealing with specifics of the FPGA (Hardware blocks (MMCM, PLL, DSP,…), IP, IO, constraints and etcetera …). Then all that is passed through the synthesis and implementation kitchen of the FPGA brand used.

Since most of my designs always need to be as compact as possible and stretch the speed boundaries this route might not be the best for me. VHDL-2008 still seems the best suited. The MyHDL way looks nice for implementation of general behaviour designs.

My 0.02 euro

What extras or other tools? ( I find additions and puzzling a normal part of design :–)

The parts you mentioned are documented in Libraries Guides, eg [1], along with instantiation templates, as I used for my MyHDL models above; copy & paste.

The MMCM looks a bit of a beast, but you only need to include the functionality you are using in the MyHDL simulation model.

I have stopped using BRAM instantiation because yosys [2], in the icestorm toolchain [3], is hot on implicit block RAM instantiation.

Which chip library part do you need to use?

What subset of functionality will you initially want for this?

Jan Coombs

[1] AMD Adaptive Computing Documentation Portal
[2] Clifford Wolf's Personal Homepage 2023 - Computers & more
[3] Project IceStorm - Claire's Homepage

You can try https://bitbucket.org/nico-dev/myhdl_xilinx_unisim_lib

Puzzling is fo rthe design, not for the tools. Tools just need to work, I can’t spend days finding out how a tool works. But I give it a second, third try.

Thanks for this!
Makes my next trials tick.