Skip to content

Raw HDL Insertion

JulianKemmerer edited this page May 30, 2022 · 22 revisions

The goal of this feature is to approximate the functionality of gcc's __asm__() function.

__asm__() is used to directly describe cpu architecture specific assembler instructions (as opposed to being compiled from higher level C language). Similarly __vhdl__() allows arbitrary VHDL to be used as PipelineC functions/modules.

uint64_t main(uint64_t x, uint64_t y) 
{
__vhdl__("\
  -- Arch declarations go here \n\
  begin \n\
  -- The arch body, processes, assignments, etc go here \n\
  return_output <= x + y; \n\
");
}

The above PipelineC produces exactly the below VHDL file.

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
use work.c_structs_pkg.all;
entity main_0CLK_d751 is
port(
 clk : in std_logic;
 x : in unsigned(63 downto 0);
 y : in unsigned(63 downto 0);
 return_output : out unsigned(63 downto 0));
end main_0CLK_d751;
architecture arch of main_0CLK_d751 is
  -- Arch declarations go here 
  begin 
  -- The arch body, processes, assignments, etc go here 
  return_output <= x + y; 
end arch;

Functions that use __vhdl__() are treated as stateful functions and will not be automatically pipelined.

Alot of hacky stuff could be done this way - however, the plan to to still have as much as possible written in PipelineC and abstracted/generated for you.

Existing IP

One primary use for __vhdl__() is to instantiate existing IP. The below example instantiates an AXIS width converter module generated from Xilinx's IP catalog.

typedef struct axis32_to_axis8_t
{
  axis8_t axis_out;
  uint1_t axis_in_ready;
}axis32_to_axis8_t;
#pragma FUNC_BLACKBOX axis32_to_axis8
axis32_to_axis8_t axis32_to_axis8(axis32_t axis_in, uint1_t axis_out_ready)
{
  __vhdl__("\n\
    COMPONENT axis_dwidth_converter_4_to_1 \n\
      PORT ( \n\
        aclk : IN STD_LOGIC; \n\
        aresetn : IN STD_LOGIC; \n\
        s_axis_tvalid : IN STD_LOGIC; \n\
        s_axis_tready : OUT STD_LOGIC; \n\
        s_axis_tdata : IN STD_LOGIC_VECTOR(31 DOWNTO 0); \n\
        s_axis_tkeep : IN STD_LOGIC_VECTOR(3 DOWNTO 0); \n\
        s_axis_tlast : IN STD_LOGIC; \n\
        m_axis_tvalid : OUT STD_LOGIC; \n\
        m_axis_tready : IN STD_LOGIC; \n\
        m_axis_tdata : OUT STD_LOGIC_VECTOR(7 DOWNTO 0); \n\
        m_axis_tkeep : OUT STD_LOGIC_VECTOR(0 DOWNTO 0); \n\
        m_axis_tlast : OUT STD_LOGIC \n\
      ); \n\
    END COMPONENT; \n\
    \n\
    begin   \n\
      \n\
    inst : axis_dwidth_converter_4_to_1  \n\
    PORT MAP (  \n\
      aclk => clk,  \n\
      aresetn => '1',  \n\
      s_axis_tvalid => axis_in.valid(0),  \n\
      s_axis_tready => return_output.axis_in_ready(0),  \n\
      s_axis_tdata => std_logic_vector(axis_in.data),  \n\
      s_axis_tkeep => std_logic_vector(axis_in.keep),  \n\
      s_axis_tlast => axis_in.last(0),  \n\
      m_axis_tvalid => return_output.axis_out.valid(0),  \n\
      m_axis_tready => axis_out_ready(0),  \n\
      unsigned(m_axis_tdata) => return_output.axis_out.data,  \n\
      --unsigned(m_axis_tkeep) => return_output.axis_out.keep,  \n\
      m_axis_tlast => return_output.axis_out.last(0)  \n\
    );  \n\
  ");
}

The above function uses a generated axis_dwidth_converter_4_to_1 component. This IP is user/project specific and is not known by the PipelineC tool during automated synthesis runs (yet). For this reason the function is marked as a FUNC_BLACKBOX whose implementation is ignored/not synthesized by the PipelineC tool (only synthesized during user bitstream generation).

Clone this wiki locally