Create a shift register custom IP as AXI-lite IP, create an RTL project and write an SDK test application code to test the IP.
Schematic Fig. 1 M-stage Shift Register
Code Below shows the entity and architecture of the shift register. Include the design into the project using Add Source. entity sh_reg is generic(N: natural; -- N-bit input M: natural);-- M stages port( x : in std_logic_vector(N-1 downto 0); z : out std_logic_vector(N-1 downto 0); ck, en, sync: in std_logic); end sh_reg; architecture Behavioral of sh_reg is type v_array is array(natural range <) of std_logic_vector(N-1 downto 0); signal temp : v_array(M-1 downto 0); begin process(ck, sync) begin if ck'event and ck='1' and sync = '1' then if en='1' then temp <= x&temp(M-1 downto 1); else for i in 0 to M-1 loop temp(i) <= (others = '0'); end loop; end if; end if; end process; Z <= temp(0); end Behavioral;
IP Core
With managing IP tool create an AXI IP and edit the sh_reg_ip_v1_0_S_AXI.vhd . The signal sync synchronizes the shifting with the slv_reg_wren (write enable) and slv_reg_ rden. Below is sh_reg_ip_v1_0_S_AXI.vhd code a portion with user component and signal declared, and the beginning of the architecture body assigning sync signal and instantiating the component sh_reg. Vivado generated code is in italic. After Synthesize and package the ip. -- Number of Slave Registers 4 Signal slv_reg0 :std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0); signal slv_reg1 :std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0); signal slv_reg2 :std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0); signal slv_reg3 :std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0); X Z signal slv_reg_rden : std_logic; signal slv_reg_wren : std_logic; signal reg_data_out :std_logic_vector(C_S_AXI_DATA_WIDTH-1 downto 0); signal byte_index : integer; -- user component and signals declare -- N-bit data M-stage shift register -- with load enable and clear when not enable component sh_reg generic(N: natural; M: natural); port( x : in std_logic_vector(N-1 downto 0); z : out std_logic_vector(N-1 downto 0); ck, en, sync: in std_logic); end component; -- synchronizing signal between -- sh_reg and software declare signal sync : std_logic; --------------------------------------------- -- Architecture body begins here --------------------------------------------- begin ------------------------------------------- -- clock enable when software -- m_write or m_read ------------------------------------------- sync <= slv_reg_wren or slv_reg_rden; ------------------------------------------- -- Instantiate user logic ------------------------------------------- U2: sh_reg generic map(32, 5) -- 32-bit data and 5-stage shift register port map(x = slv_reg0, z = slv_reg1, en = slv_reg2(0), sync = sync ,ck = S_AXI_ACLK); ------------------------------------------- Multiple Drives on slv_reg1 Since slv_reg1 is the register to which the IP Core assigns the output port z, comment to the assignments of slv_reg1 in the write process to avoid multiple drivers error as shown below. -- Implement memory mapped register … process (S_AXI_ACLK) variable loc_addr :std_logic_vector(OPT_MEM_ADDR_BITS downto 0); begin if rising_edge(S_AXI_ACLK) then if S_AXI_ARESETN = '0' then slv_reg0 <= (others = '0'); --slv_reg1 <= (others = '0'); … -- when b"01" = … -- end loop; when b"10" = … when others = slv_reg0 <= slv_reg0; --slv_reg1 <= slv_reg1; … end process; Block Diagram Figure 2 shows a block diagram of the system with the shift register custom IP - sh_reg_ip. Fig. 2 Vivado Block Diagram
Test Bench Build a project which has sh_reg_ip as an AXI peripheral and use SDK to verify the correctness of the shift register IP core. Below is an example of a test application code which writes to slv_reg0 and reads from slv_reg1.
Test Application Code
Terminal Output Note that slv_reg0 and slv_reg1 add additional stages to the shift register. The test application writes to slv_reg0 which becomes the leftmost stage. However, the slv_reg1 has the same content as the shift register rightmost stage.