Jump to content

How to use and manage memory with my FPGA?


GalD101

Recommended Posts

Ok. The reason I asked for details on how you tested is mostly that I want to understand the signal levels you have used, and the way you deal with impedance/termination. It is also important to understand to which pins of your Spartan-7 board things are hooked up, because not all pins of the board are connected in an identical way to the FPGA (in particular, there's a difference between the PMOD pins and the other PIO pins). That information is, unfortunately, not possible to get from your photos.

Your function generator is an SRS 345. At 30 MHz bandwidth it's a bit slow as a stand-in for the SPD -- you will probably not be able to make super short pulses with it. But it will have to do I guess.

EDIT

The SPD is a brand I've used before (but a different model), and indeed it will just produce short pulses at 5V with 50 Ohm source impedance. If you'd hook it up to a scope, you would see something very similar to the image I posted before.

Edited by reddish
Link to comment
Share on other sites

> I connected the function generator to pio26,

I assume you connected directly, without regard for 50 Ohm termination?

What kind of signal did you generate? And how did you observe that it works?


If the function generator is to act as a stand-in for the SPD, you will have to make it generate 5V (as the SPD will do), and figure out a way to get that safely into the FPGA, without exceeding its safe input levels. I have proposed a way of doing that some posts ago, but it seems you're not doing what I proposed there.

The point of this small exercise is that you show that you understand all this well enough to not blow up your FPGA once you hook up the SPD later.

Edited by reddish
Link to comment
Share on other sites

12 minutes ago, reddish said:

I assume you connected directly, without regard for 50 Ohm termination?

correct.

 

I thought that for now I will use the function generator without worrying about impedance and blowing up the FPGA (as my professor told me to, since he wants me to mainly focus on programming the FPGA) so I thought that for now I will just try to learn how to program the FPGA and understand how to control it's clock and how to write to memory. I apologize for my misunderstanding.

Link to comment
Share on other sites

Okay, we'll disregard the technicalities of safely hooking up the SPD to your FPGA board for now.

On your picture I see that you use your probe (which is a sensing device) to deliver a signal from your function generator to your FPGA. Please don't do that. An oscilloscope probe is /not/ a stand-in for two wires.

Do you have a local lab person to ask questions to? Please ask them for advice on how to properly connect stuff.

Link to comment
Share on other sites

2 minutes ago, GalD101 said:

I think the threshold is 500 MHz but I'm not sure. Here's a picture

No that's the limit for the OUTPUT frequency. I'm typing a small explanation of the MMCM block right now, that will help to understand what you're doing. Give me 30 minutes or so.

Link to comment
Share on other sites

Right, let's dive into the MMCM (Mixed-Mode Clock Manager). This is a piece of functionality built into your FPGA that allows you to synthesize output frequencies from input frequencies.

Here's an annotated image taken from UG472 that shows how the MMCM block works:

image.png.54574a9ac6bc52b100b269bbf426b26f.png


In green, I indicated things that you, the user, can configure: a global value D, a global value M, and a channel-specific divide which I will refer to as DIV_CLK0.

In red, I indicated a few places where signals with a certain frequency live. f_in is the input frequency. In your case, this will be 12 MHz coming from a crystal on your FPGA board. Frequency f_out is the output frequency, which we want to be 100 MHz, which is 8.33x the input frequency. Seems like quite a difficult task.

Okay, here are the important ideas that make the MMCM tick (pun intended):

* First: given a HIGH frequency, it is quite easy to make a LOWER frequency that is lower by some integer number N. You do this by using a so-called DIVIDER that watches its input, counts off edges, and toggles its output every N'th edge. This is realizable in hardware by having a counter and a bit of electronics.
* Then, we have a Voltage Controlled Oscillator (VCO) the frequency of which is, well, controlled by a voltage, as the name implies.
* We can manipulate the VCO frequency by applying a voltage. If we see the frequency of the VCO is too high, we lower the voltage a bit; if we see the frequency of the VCO is too low, we increase the voltage a bit. This is a small control system, and in the MMCM you see this by the small chain of PFD/CP/LF components.

In the system drawn above, there are three important dividers:

* D divides the input frequency and produces a frequency f_pfd == f_in / D.
* M divides the VCO-generated frequency and produces a frequency f_vco / M.
* DIV_CLK0 divides the VCO-generated frequency and produces a frequency f_out == f_vco / DIV_CLK0.

Now here's the trick. The little chain of PFD (phase-frequency detector), CP (charge pump), and LF (loop filter), looks at its input frequencies f_pfd and (f_vco/M), the latter being feedback from the output of the M-divider into the PFD (check the picture).

The (PFD/CP/LF) combo will then start to manipulate the input voltage to the VCO, to equalize its input frequency (f_vco/M) and f_pfd. It's a control system with feedback, and in short time, it will achieve a lock, where (f_vco/M) and f_pfd are identical. Given that f_pfd == f_in / D, we have the beautiful result:

(f_vco / M) == (f_in / D)

Or, even more nicely:

f_vco == f_in * (M / D)

Now you understand why M is called M, and D is called D. They are a "multiplier" and a "divider" for the input frequency.

Now the interesting puzzle is that there are all kinds of constraints in play for the frequencies and the user-settable inputs. For your Spartan-7, the frequency requirements can be found here. In summary:

f_in must be between 10 and 800 MHz. ours is 12 MHz, so we're good.
f_vco must be between 600 and 1200 MHz.
f_out must be between 4.69 and 800 MHz. We want 100 MHz, so no problem.
f_pfd must be between 10 and 450 MHz.

Now our user-configurable values M, D, and DIV_CLK0 also have constraints; they can only take from a certain set of values.

This can be quite a puzzle especially if you want to generate weird frequencies, but I'll just tell you the solution for what you want to do.

You will put D to 1, making f_pfd identical to f_in, so 12 MHz.
you will put M to 50, making f_vco equal to 600 MHz, once the lock is achieved.
you will put DIV_CLK0 to 6, dividing the 600 MHz down to 100 MHz.

And there you have it: With those parameter values, you will generate a 100 MHz clock from a 12 MHz input clock.

Okay now how to do this?

One way is to use the "clocking wizard" in Vivado to get it to make your clock. You've been toying with that, and it should work with some massageing.

An alternative (which is what I do), is to instantiate an MMCM block in your Verilog code, and put the parameters there.

If you prefer the clocking wizard, by all means go ahead. If you prefer to learn how to instantiate an IP block from Verilog, let me know, and I will talk you through that.

(As an aside: the reason I prefer to instantiate the block inside HDL code is that I find all the "IP wizards" complicated, error-prone, and quite cumbersome in practice; they save some time in the short term, but in the long term they are less efficient, at least for me. Being a programmer, I prefer my entire design to be represented in source code, rather than some undocumented internal format conjured up by Xilinx. But for you it may be easier to use those wizards.)


Let me know when you think you have a 100 MHz signal in your FPGA. Put it on a pin, and send it to your Hameg. It's a pretty shitty oscilloscope my modern standards, but you should see something that looks like a weak 100 MHz sinewave. Don't trust it beyond that though; you're feeding a signal that exceeds the bandwidth of your scope by quite some margin. I'd recommend you ask the local lab people if there isn't some low-end digital 100 or 200 MHz scope available for your project, that should help a lot.
 

Link to comment
Share on other sites

Okay. The clock wizard has instantiated an "MMCME2_ADV" entity. There's also a "MMCME2_BASE" which is a simplified version that would have been sufficient for your application, but this will do for now.

You seem to have put the CLKOUT0_DUTY_CYCLE  value at 0.333. Just put it at 0.5, which is the default for clocks. You just want to generate a regular 100 MHz clock here; by convention, clocks should have a duty cycle of 50%.

Also, please clean up the code generated by the wizard. The input and output names should be sensible for your design; I'd suggest CLK_IN_12MHz and CLK_OUT_100MHz.

You will have to comment out a pin definition in your XDC file where you want to output the 100 MHz signal.

What you want to do is instantiate the clk_wiz_0_clk_wiz module in your toplevel design. The idea is that you can instantiate sub-modules in a module, which provides a good way to divide your system into sub-systems. The "clock synthesizer" module you're working on now should be a sub-module of your toplevel ("main") Verilog module. If you don't understand this, stop and read the first few chapters of an introductory Verilog book.

When instantiating your clock synthesis sub-module, you should hook up its 'clk_in1' input to the 12 MHz clock signal, and the 'clk_out1' to the pin you commented out for that purpose in the XDC file. Connect the RESET input to '0'. To see the signal, you must connect the Hameg's probe to the pin.

Unfortunately, I can't tell you the syntax for instantiating a sub-module in Verilog. Please try to find that in your Verilog book of choice, or google for it. I may look into it later.

Link to comment
Share on other sites

13 minutes ago, reddish said:

If you don't understand this

I think I understand, I need to instantiate a module of clk_wiz_0_clk_wiz  in a new file which I'll name top.v like so:
clk_wiz_0_clk_wiz  clk_wiz_instance (
.clk_out1(CLK_OUT1),
.reset(RESET),
.locked(LOCKED),
.clk_in1(CLK_IN1)
)

The question is, what values should I supply to this? (what parameters do I need to supply instead of the capital letter ones?)
does CLK_IN1 needs to be the original 12 MHz clock (that is, this one?: set_property -dict { PACKAGE_PIN M9    IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L13P_T2_MRCC_14 Sch=gclk)
does RESET needs to be a certain btn?
what is LOCKED?
does CLK_OUT1 needs to be a certain pin of my choice?

Link to comment
Share on other sites

Based on the schematic, I'd suggest using any of the PIOxx pins. Those have clipping diodes, making them a bit more resilient against abuse than the PMOD pins. Other than that, it doesn't really matter.

EDIT

When instantiating, put the pin on which your 12 MHz clock enters the FPGA for CLK_IN1, and put the PIO pin you selected for output for CLK_OUT1. The RESET pin, you can fix that to 0, disabling it. At this stage, there's no need to bother with a reset for the clock synthesizer.

LOCKED is an output of the MMCM block, indicating whether the control loop has locked (so you can trust the clock outputs). Ignore that for now.  But I do recommend that you put .STARTUP_WAIT("TRUE") in the clock synthesizer definition instead of .STARTUP_WAIT("FALSE"). That way, the FPGA will wait until all clocks have stabilized before starting operations. That's a good idea.

Edited by reddish
Link to comment
Share on other sites

13 hours ago, reddish said:

generate a 30% duty cycle

 

4 hours ago, reddish said:

30% duty cycle output

  

24 minutes ago, reddish said:

clocks should have a duty cycle of 50%

you earlier proposed to use a duty cycle of 30%. May I ask what is the meaning of it and why does it need to be 50%?

Link to comment
Share on other sites

I want you to generate a signal with a duty cycle of 30%, but we'll do that programmatically, while cycling through a 10-state state machine at 100 MHz, and generating 1-1-1-0-0-0-0-0-0-0, giving you a repetitive pulse train that, on the scope, will look like a 30%, 10 MHz clock. But that signal is not a "clock" in the technical sense; we'll not use it as a clock input to any flip-flop. Sorry for the confusion. For now, just generate a 100 MHz 50% duty cycle clock. The next step will be to use that to make a 30%, 10 MHz signal.

EDIT 2

Also, the "duty cycle" you can specify in the MMCM block only works if it is specified in eights, so 0.125, 0.250, 0.375, 0.500, 0.625, 0.750, 0.875. To understand why requires a more detailed model for the MMCM than what is useful at this time.

For a 30% duty cycle signal, we will have to do something else.
 

Edited by reddish
Link to comment
Share on other sites

I changed the clock in the xdc file:
set_property -dict { PACKAGE_PIN M9    IOSTANDARD LVCMOS33 } [get_ports { CLK_IN_12MHz }]; #IO_L13P_T2_MRCC_14 Sch=gclk
create_clock -add -name sys_clk_pin -period 83.33 -waveform {0 41.66} [get_ports { CLK_IN_12MHz }];

set the clockout to pio26

set_property -dict { PACKAGE_PIN L14   IOSTANDARD LVCMOS33 } [get_ports { CLK_OUT_100MHz }]; #IO_L7N_T1_D10_14 Sch=pio[26]
 

and that should be it for the constraints file correct?

Link to comment
Share on other sites

1 minute ago, GalD101 said:

so I should instantiate it to 0 similar to reset? (.locked(0))

I think in Verilog you can just say .locked(), with empty parentheses.

RESET is an input to the clock synthesizer; LOCKED is an output. If you think about it like that, it makes no sense to instantiate the clock synthesizer with locked(0).

Link to comment
Share on other sites

1 minute ago, GalD101 said:

I changed the clock in the xdc file:
set_property -dict { PACKAGE_PIN M9    IOSTANDARD LVCMOS33 } [get_ports { CLK_IN_12MHz }]; #IO_L13P_T2_MRCC_14 Sch=gclk
create_clock -add -name sys_clk_pin -period 83.33 -waveform {0 41.66} [get_ports { CLK_IN_12MHz }];

set the clockout to pio26

set_property -dict { PACKAGE_PIN L14   IOSTANDARD LVCMOS33 } [get_ports { CLK_OUT_100MHz }]; #IO_L7N_T1_D10_14 Sch=pio[26]
 

and that should be it for the constraints file correct?

The former is good.

The latter is not good, but I understand why you tried it. In principle, good thinking, but the second 'create clock' is not necessary.

Adding a "create_clock" directive in the XDC file informs that some external thing (like a crystal) is driving the pin.

In what we're doing now, the CLK_OUT_100MHz is generated by the FPGA itself, so not by some external thing. Hence, we don't need to tell Vivadio about it and put it in the XDC file. Vivado could derive from the input clock, the MMCM definition, and the fact that you hooked up the output of the MMCM to the pin, that there is a 100 MHz clock there if it wanted to (but it doesn't really need that particular tidbit of information).

I realize this is confusing and seems a bit arbitrary. We're covering a lot of ground in a few posts; when I learned this stuff, I had months to absorb it all. Don't be distraught that a few of these things we're doing seem a bit arbitrary. You don't have a "big picture" mental model yet of what we're doing, where these kinds of details make sense. But with practice, you will get there.

 

Link to comment
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
×
×
  • Create New...