Problems using a Dual access on-chip memory module (QSYS)
Hello,
I have a QSYS system using a Nios II/e processor along with some vhdl modules which should be able to display pixels on a LCD display using a VGA controller. I have already managed to get the Nios II code working as well as the VGA interface.
I now seek to access the memory set by the Nios II code in VHDL to retrieve the pixel data and pass them onto the VGA interface, this should be possible with the dual access memory module in QSYS.
This is the toplevel VHDL code:
LIBRARY ieee; USE ieee.std_logic_1164.all; use ieee.numeric_std.all; ENTITY niosdisplay IS PORT( clk : IN STD_LOGIC; key : IN STD_LOGIC; pixel_clk : OUT STD_LOGIC; red : OUT STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '1'); --red magnitude output to DAC green : OUT STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '1'); --green magnitude output to DAC blue : OUT STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '1'); --blue magnitude output to DAC btn : IN STD_LOGIC_VECTOR(3 DOWNTO 0) := (OTHERS => '0'); --btn pio h_sync : OUT STD_LOGIC; v_sync : OUT STD_LOGIC; n_blank : OUT STD_LOGIC; n_sync : OUT STD_LOGIC ); END niosdisplay; ARCHITECTURE behavior OF niosdisplay IS COMPONENT vga_controller IS PORT( pixel_clk : IN STD_LOGIC; --pixel clock at frequency of VGA mode being used reset_n : IN STD_LOGIC; --active low asycnchronous reset h_sync : OUT STD_LOGIC; --horiztonal sync pulse v_sync : OUT STD_LOGIC; --vertical sync pulse disp_ena : OUT STD_LOGIC; --display enable ('1' = display time, '0' = blanking time) column : OUT INTEGER; --horizontal pixel coordinate row : OUT INTEGER; --vertical pixel coordinate n_blank : OUT STD_LOGIC; --direct blacking output to DAC n_sync : OUT STD_LOGIC --sync-on-green output to DAC ); END COMPONENT vga_controller; COMPONENT altpll0 IS PORT ( areset : IN STD_LOGIC := '0'; inclk0 : IN STD_LOGIC := '0'; c0 : OUT STD_LOGIC ); END COMPONENT altpll0; COMPONENT system is port ( btn_pio_export : in std_logic_vector(3 downto 0) := (others => '0'); -- btn_pio.export clk_clk : in std_logic := '0'; -- clk.clk frame_buf_address : in std_logic_vector(16 downto 0) := (others => '0'); -- frame_buf.address frame_buf_chipselect : in std_logic := '0'; -- .chipselect frame_buf_clken : in std_logic := '0'; -- .clken frame_buf_write : in std_logic := '0'; -- .write frame_buf_readdata : out std_logic_vector(31 downto 0); -- .readdata frame_buf_writedata : in std_logic_vector(31 downto 0) := (others => '0'); -- .writedata frame_buf_byteenable : in std_logic_vector(3 downto 0) := (others => '0'); -- .byteenable reset_reset_n : in std_logic := '0' -- reset.reset_n ); END COMPONENT system; --VGA Stuff signal clk_138 : std_logic; signal column : integer; signal row : integer; signal disp_ena : std_logic; --Nios II Stuff signal mem_data : std_logic_vector(31 downto 0) := (others => '0'); signal frame_buf_addr : natural range 0 to 65536 := 0; BEGIN u0: altpll0 port map('0', clk, clk_138); u1: vga_controller port map(clk_138, '1', h_sync,v_sync, disp_ena, column, row, n_blank, n_sync); u2: system port map(btn_pio_export => btn, clk_clk => clk_138, reset_reset_n => '1', frame_buf_address => std_logic_vector(to_unsigned(frame_buf_addr, 17)), frame_buf_readdata => mem_data, frame_buf_chipselect => '0' ); pixel_clk <= clk_138; draw_pixel : PROCESS(clk_138) variable count : natural range 0 to 4 := 0; BEGIN IF rising_edge(clk_138) then IF disp_ena = '1' then --IF((row > 256) and (row < 256+512) and (column > 320) and (column < 320+512)) THEN --Inside game screen case count is when 0 => red <= mem_data(31 downto 24); blue <= mem_data(31 downto 24); green <= mem_data(31 downto 24); count := 1; when 1 => red <= mem_data(23 downto 16); green <= mem_data(23 downto 16); blue <= mem_data(23 downto 16); count := 2; when 2 => red <= mem_data(15 downto 8); green <= mem_data(15 downto 8); blue <= mem_data(15 downto 8); count := 3; if(frame_buf_addr = 65535) then frame_buf_addr <= 0; else frame_buf_addr <= frame_buf_addr + 1; end if; when 3 => red <= mem_data(7 downto 0); green <= mem_data(7 downto 0); blue <= mem_data(7 downto 0); count := 0; when others => red <= mem_data(31 downto 24); green <= mem_data(31 downto 24); blue <= mem_data(31 downto 24); end case; --ELSE --Gray color to boundary --red <= "11111001"; --green <= "10101001"; --blue <= "10101001"; --END IF; ELSE red <= (OTHERS => '0'); green <= (OTHERS => '0'); blue <= (OTHERS => '0'); END IF; END IF; END PROCESS; END behavior;
This is the Qsys system:
The code increments the address every 4 accesses so it sets a single byte every 4 clock cycles. But for some reason the readdata port of the memory module is not changed whenever the address changes. It seems to be stuck at address 0, this results in the screen having the same 4 pixel pattern throughout the screen.
Here is a screenshot of a signal tap I did:
As is visible in the picture, the frame_buffer_readdata does not change its data to the corresponding frame_buf_addr. I have tried playing with the byteenable and chipselect signals but nothing seems to work getting the module to respond to the address change.
Does anyone have any idea what is wrong or what I am missing here?
Thanks,
Mart
So I finally solved the issue...
The problem was that the frame_buffer address immediately changed back after 1 clock cycle. Since the memory has a delay it needs to be kept at their desired value for at least 2 clocks cycles, doing this will result in the readdata value changing in the third clock cycle. I have another post on reddit with a bit more context:
https://www.reddit.com/r/FPGA/comments/y1fkb1/problem_accessing_sdram_from_nios_iie_and_vhdl/