SergeyS.

SDRAM controller for Papilio Pro

16 posts in this topic

Hello,

I'm a newbie in the FPGA, and I'm managing with the Papilio Pro Board. The problem is I can't make the SDRAM work correctly. I was trying to use the Hamster's SDRAM controller, and did as he describes, but I get weird results.

First, sometimes it reads with random mistakes (though it might be a write problem, I don't know)

Second, sometimes during sequential read it reads first 3-4 values the same and only then starts to increment address.

Could you please advise some VHDL code example of how to explore the Hamster's SDRAM controller (or any other)?

Hamsterworks wiki site doesn't work for some reason so I can't read his explanation once more...

Kind regards,

Sergey

Share this post


Link to post
Share on other sites

SDRAM isn't something I'd recommend a newbie try to deal with.  It is tricky, and further complicated by the fact that until read and write both work, nothing works.  Starting with simpler projects is recommended.

I can't point you to any examples meant for teaching, but here are some existing projects, which use the SDRAM, that come to mind; they might be informative.  "socz80" at http://sowerbutts.com/socz80/ uses Hamster's SDRAM controller with a Z80 CPU.  The "ZPUino" port for Papilio Pro also uses that controller, see https://github.com/alvieboy/ZPUino-HDL/blob/master/zpu/hdl/zpuino/boards/papilio-pro/S6LX9/variants/vanilla/sdram_hamster.vhd and https://github.com/alvieboy/ZPUino-HDL/blob/master/zpu/hdl/zpuino/boards/papilio-pro/S6LX9/variants/vanilla/sdram_wrap.vhd.  These might also be using different versions of the controller.

I believe clock rate matters for this controller,  ZPUino runs at 96MHz, socz80 at 128MHz; at much lower clock rates it won't operate right.

I've found that anything on an FPGA requires a lot of debugging, it's just something you have to do.

Share this post


Link to post
Share on other sites

Try my (ZPUino) SDRAM controller.
You can find here: https://github.com/alvieboy/ZPUino-HDL/tree/master/zpu/hdl/zpuino/memory/sdram

It uses pipelined wishbone, so make sure you don't hold STB high after your request has been accepted. Also take a look at the PLL settings for it https://github.com/alvieboy/ZPUino-HDL/blob/master/zpu/hdl/zpuino/boards/papilio-pro/S6LX9/clkgen.vhd

Any issue drop me an email at alvieboy at alvie dot com

Alvie

Share this post


Link to post
Share on other sites

Hi, the latest version of Hamster Controller on his website did not work without a slight modification on the Papilio.

Here is a corrected version, which works for me in several designs on the Papilio:

https://github.com/bonfireprocessor/bonfire-soc/blob/master/sdram/SDRAM_Controller.vhd

The Fix is the "delay_line_length " parameter, which must be 5 for the Papilio. Clock frequency should be ~100Mhz. My version also has an "hold_row_open " parameter, which can improve performance in some applications (end reduce in others...). It holds the active row open until a different row or a refresh is needed. 
 

I also strongly recommend simulating the design, Hamster also has an sdram simulator (you can also get a slightly modified version here: https://github.com/bonfireprocessor/bonfire-soc/blob/master/sdram/sdram_model.vhd).
You can also check with a simulation if your access to the controller works in the right way. If you like I can post a few simulation screen shots here to show how it should work. 

You should also install a bit file of a proven design, like socz80, Zpuino or for example: 

 

(this post contains a "monitor.bit" which you can upload into the Papilio. The boot monitor contains a DRAM test which can be started with the "T" command. 
It is just that you can verify that your Papilio Pro board has no hardware defect (unlikely but not impossible...)

Thomas
 

 

 

 

 

Share this post


Link to post
Share on other sites

Hello to all,

Thank you very much for your advises. I was trying to understand both Hamster's SDRAM controllers (simple and extended (ZPuino one)). The last one seems to be quite difficult for understanding for me so far. 

Alvie, could you please give some hints why this huge controller is so... huge? I mean what are its advantages before the simple one? (sorry if my question seems stupid)

I made some success with the simple SDRAM controller, though I still have some random misreads. 

Kind regards,

Sergey

Share this post


Link to post
Share on other sites

Hi Sergey,

without referring to the actual source code it is hard to understand to what version of the controller you refer to (especially the "simple" one). As you noticed Hamsters Website is offline, so there is no documentation anymore. 

What I remember from my mind is, that he developed several version of the controller, they differ in complexity of their state machines and their performance. The most basic one he wrote just executed every DRAM access isolated (with opening the row, opening the column, reading/writing and then closing the row and going back to idle state). This is very slow, because every access to the DRAM requires more than 20 clock cycles. 

The more advanced versions support back-to-back read/writes, which means that several sequential accesses to adjacent addresses of the DRAM can be run much faster. 

Actually the latest version of his controller is not really "huge", it is around 550 lines of code and it is well commented. To understand it, you should read the SDRAM chips datasheet it describes quite well how it works. Once you understand it, hamsters controller looks quite straight forward. You should consider using my version of it (see link above), it worked in several Papilio Pro designs right out of the box. I usually use a clock frequency of 96Mhz, believe me, it works. 

I'm sure the Alvies version in zpuino is equally proven, looking in the source code I think it is based on Hamsters "older" design. It requires two phase shifted clocks (which can easily by accomplished), while the 0.6 version creates a phase 180 degrees phase shifted clock internally with the help of a ODDR2 block. The shifted clock is required to compensate for the pcb trace delays and ensuring that the control and data signals are stable when the clock arrives at the DRAM chip ("setup and hold times").  The zupino variant with the extra clock is more complex, but maybe works over a wider clock frequency range, because the phase shift does not depend on the frequency, it keeps constant.  

 I never tried different versions of Hamsters controller, this latest ensures for example that the Flip-Flops in the IO Blocks of the FPGA are used, this is very important for reliable operation. You can verify this in the "Design Overview /IOB Properties" Node in ISE: In Column "Regs" "IFF", "OFF" or both of them must be reported for Inputs/Outputs/Bidirectionals. 

Are you using the SDRAM Controller directly or do you have e.g. a Wishbone interface in between?

Have you tried my advice and simulated the design?

 

Share this post


Link to post
Share on other sites

Hello Thomas

Sorry for not responding for such a long time, I was working on other project, and recently returned to FPGA. I followed your recommendations and finally got the SDRAM controller worked. I used Hamster's controller with your modification and the Wishbone interface from your Bonfire project. Thank you very much for your support!

Though now I faced another issue. My project works as follows. It receives data via SPI interface and saves it into SDRAM, then after some event occurs it reads data from RAM into array block-wise. The problem is that it works very unstable, sometimes the amount of send and received bytes doesn't match, sometimes it doesn't receive data at all. I was thinking it's the problem of my SPI driver, but I used several of them and still no effect.

Also I was trying to simulate the code and it works perfectly all the time. I was monitoring the signals with the logic analyzer, and it also shows no problem with SPI interface, no jitter, no extra pulses. And the issue doesn't depend on the SPI data rate.

Here's the code which implements operations with SPI and SDRAM. Maybe I do something wrong here, and don't notice it due to the lack of experience in VHDL.

RAM_process: process (clk_100)
		begin
			if rising_edge (clk_100) then
				case RAM_state is
				-------------------waiting for command------------------------
					when R_idle =>
						
						if spi_data_ready = '1' and spi_data_ready_prev = '0' then
							wbs_dat_i <= spi_rx_buffer;
							RAM_state <= R_write;
							wbs_we_i <= '1';
							wbs_stb_i <= '1';
							wbs_cyc_i <= '1';
							spi_receive_counter <= spi_receive_counter + 1;	
						end if;	
						spi_data_ready_prev <= spi_data_ready;
					
						if print_process = '0' then
							read_addr <= (others => '0');
						end if;	
							
						if start_read_from_RAM = '1' then 
							RAM_state <= R_read;
							wbs_cyc_i <= '1';
							wbs_stb_i <= '1';
							wbs_we_i <= '0';
							word_counter <= (others => '0');
						end if;
						
						if cs = '1' then 
							wbs_stb_i <= '0';
							wbs_cyc_i <= '0';
							wbs_we_i <= '0';
							received_row_counter <= std_logic_vector(to_unsigned(to_integer(unsigned(spi_receive_counter) / 11), received_row_counter'length));
							RAM_state <= R_disable;
						end if;	
					------------------ write data to memory---------------	
					when R_write =>
						if wbs_ack_o = '1' then	
							write_addr <= write_addr + 1;
							RAM_state <= R_idle;
						else
							RAM_state <= R_write;
						end if;
					------------------ read data from memory---------------	
					when R_read =>
						if wbs_ack_o = '1' then
							if word_counter = "10110" then
								if start_read_from_RAM = '0' then 
									wbs_cyc_i <= '0';
									wbs_stb_i <= '0';
									if cs = '0' then
										RAM_state <= R_idle;
									else
										RAM_state <= R_disable;
									end if;
								end if;	
							else 
								print_data (to_integer(unsigned(word_counter))) <= wbs_dat_o (31 downto 16);
								print_data (to_integer(unsigned(word_counter)) + 1) <= wbs_dat_o (15 downto 0);
								word_counter <= word_counter + 2;
								read_addr <= read_addr + 1;
							end if;	
						else
							RAM_state <= R_read;
						end if;
					------------------ disabled SPI interface---------------	
					when R_disable =>
						
						if cs = '0' then
							write_addr <= (others => '0');
							spi_receive_counter <= (others => '0');
							RAM_state <= R_idle;
						end if;	
						
						if start_read_from_RAM = '1' then 
							RAM_state <= R_read;
							wbs_cyc_i <= '1';
							wbs_stb_i <= '1';
							wbs_we_i <= '0';
							word_counter <= (others => '0');
						end if;
						
						if print_process = '0' then
							read_addr <= (others => '0');
						end if;	

					when others =>
						RAM_state <= R_idle;		
				end case;
			end if;	
		end process;

Could anyone please help me?...

Kind regards,

Sergey

Share this post


Link to post
Share on other sites

Hi Sergey,

it will be hard to help you without having a look in your whole design. I'm missing also a lot of information about e.g. the data rate, the clock frequencies, the amount of data. It would also help to post e.g. simulation waveforms of the SPI and Wishbone interfaces.

As an general advice I suggest you to strip down your design to the key building blocks and test them separately. You can also do this on the hardware. When you start with a design using block RAMs you can avoid a lot of pitfalls. BRAMs are dual ported so you can easily use read and write processes without taking care of data contention. The LX9 has 64KByte of BRAMs so it should be enough for some basic testing. 

Regarding simulation you should build unit tests for the parts of your design and check how they work.

Maybe you can look at https://github.com/bonfireprocessor/bonfire-dcache/blob/master/tb_dcache.vhd as an example. It contains also test code to "consume" data from a wishbone master (process mem_simul).

 

Thomas 

 

Share this post


Link to post
Share on other sites

Hello Thomas,

I'm not sure if I allowed to send the whole design due to NDA. Though I can share required information. The SPI data rate is 500 kHz to 10 MHz (I tested in this range, and result is the same). SDRAM works with 100 MHz clock frequency. The amount of data is tens - thousands of kbits.

The start of the SPI transaction is showed in the simulation below. You can see both SPI data and Wishbone data in it.

image.thumb.png.72df15039ad44556249ba924bd54b199.png

And here's the finish of the SPI transaction

image.thumb.png.60ae535e906140aeb953b088691b2ee4.png

Here I send 220 x 32 = 7040 bits.

The simulation code is

cs <= '0';
		-------------------------------
		
		for i in 0 to 219 loop
		
			temp <= std_logic_vector(to_unsigned(i, temp'length));
		
			for j in 0 to 31 loop
		
				sck <= '1';
				if j = 0 then
					mosi <= '0'; 
				else mosi <= temp(31);
				end if;
				wait for 10 ns;
				
				temp <= temp(30 downto 0) & '0';
		
				sck <= '0';
				wait for 10 ns;
				
			end loop;	
		
		end loop;
				
		wait for 10 ns;
			
		cs <= '1';

I don't know if this information is useful though...

Kind regards,

Sergey

Share this post


Link to post
Share on other sites

Also I've noticed a weird thing.

Some background first. The device I design is the cartridge driver which should receive data via SPI from the MCU and then read and send to cartridge.

So the weird thing is when I add the delay between data I send to the cartridge to see in the display what actually I received, it works good all the time. I receive the correct amount of data and the data itself is correct. But when I remove this delay to print data with the high speed the problem returns. I don't know how this part can influent on data read from SPI. There is an independent process which controls the cartridge, and it starts only on demand and after the SPI transaction is finished.

Maybe there is some internal interference in the FPGA structure, I have no idea...

Share this post


Link to post
Share on other sites

Hi Sergey,

your wavewforms look a bit strange...

First I see some "undefined" values (where the waveform is in the middle between 0 and 1). In a real circuit there are no "undefined" values, in an FPGA it will be either 0 or 1. When your code has something like 

if signal='0' then
...
endif

it will never execute the then part in a simulation when signal  is e.g. 'X' or 'U' , but in a real circuit it will most likely fulfill the condition. So you should clean you design in the simulation that it does not contain undefined values expect in areas where you really expect it (e.g. on reset...).

Than I'm wondering about your wishbone ack signal. I assume that every 1 pulse of the spi_data_ready is a new data word. Then I would expect one Wishbone write transaction with exactly one ack pulse. Put you have a lot of them without any change in data and address. So you write the same word a lot of times. This itself will not directly doing any harm, but it gives me a hint that your design is not working as intended.

Maybe you should take a look into the Wishbone B4 spec (just google it please...)

 

Thomas

 

 

 

Share this post


Link to post
Share on other sites
Quote

The device I design is the cartridge driver which should receive data via SPI from the MCU and then read and send to cartridge.

What do you mean with "cartridge"?

Share this post


Link to post
Share on other sites

I've made the changes you mentioned regarding the simulation. Now it looks like this

image.thumb.png.df8f3597620f011895bf0f21cfeff2d6.png

wbs_dat_o is still undefined by this moment, thus it remains 'U'.

And this hasn't solved the hardware issue...

Share this post


Link to post
Share on other sites

OK, seems like I've figured out the root of the issue.

This is the SPI signal from MCU when FPGA is powered off

image.thumb.png.7df2cd401277825901226ec87d2e5584.png

And this is what I have when I power FPGA on

image.thumb.png.7495e49d706685676f653eb662afe74d.png

This is zoomed image

image.thumb.png.5670269cbe2f670c1decc63845d63d12.png

What can cause those pulses?

The constraints file is included

Kind regards,

Sergey

constraints.ucf

Share this post


Link to post
Share on other sites

OK, the problem was in my SPI driver. I changed MISO on the falling edge instead of rising edge.

Now it seems like issue is solved.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now