Jump to content

How to use and manage memory with my FPGA?


GalD101

Recommended Posts

9 minutes ago, GalD101 said:

so all that is left to do now is to synthesize the code, run implementation, generate bit stream, program the device and connect pio26 to the oscilloscope and hope for a sine wave to show up?

Yes! Ideally, you'd see a 100 MHz block wave, but your scope cannot do those sharp edges, so it will be more like a sine. The important thing is that we want to see 100 MHz.

Fingers crossed ...

EDIT

Note that I'm playing along here with a CMOD-A7 (very similar to the CMOD-S7). It's a nice opportunity for me to get some exposure to Verilog. And I am indeed seeing a mildly deformed 100 MHz, so I'm curious if you succeed as well.

Edited by reddish
Link to comment
Share on other sites

16 minutes ago, GalD101 said:

that's really weird since the clock wizard generated this file too with this line:
#create_clock -period 83.333 [get_ports clk_in1]

clk_wiz_0_ooc.xdc 2.43 kB · 0 downloads

What do you find weird about that?

The clock wizard suggests that you put a create_clock in your 12 MHz clock INPUT pin. You already have this constraint in your manually edited XDC file, right?

It would be weird if the wizard suggested a create_clock on the output pin.

 

 

Link to comment
Share on other sites

21 minutes ago, GalD101 said:

ughhhh.... For some reason there is a failure at the generate bitstream level
 it doesn't log any error that's odd
these are the two sources files with top.v set as the top module. Am I missing something?
The only thing I can think about is the .locked() thing. Can I instantiate it to something else? I'm still not 100% sure what even is the purpose of it

I'm gonna let you swim a bit here. Learning to deal with unexpected behavior of Vivado and double-checking everything you did is a part of the learning experience I'm afraid. Yeah I known -- bit of an asshole move. But really, if you just defer to me for any and all small hiccups, (1) you will learn less, and (2) I will get fed up with this exercise really quickly.

Of course, feel free to ask pointed questions. But just dropping sources in my lop and saying "help" is a bit too broad.

The LOCKED signal coming out of the MMCM indicates that its output clock is stable. If you want to check if it solves anything, tie it to your LED output.

Edited by reddish
Link to comment
Share on other sites

3 minutes ago, GalD101 said:

i get no signal on the Hameg 😞

Ok.

Try to get a better scope, at least 100 MHz bandwidth, perferably 200 MHz +.

If you still see no signal, carefully review your sources (XDC and verilog) to find the issue.

If you find no issue, attach the most recent versions of the Verilog and XDC files and I will look at them.


As a reference, here's what I am seeing with my CMOD-A7 connected to a 200 MHz scope:

image.thumb.png.a453cfa6b4d9891cb9c0bcdb52fc93b6.png


You should be pretty close to this.
 

Link to comment
Share on other sites

7 minutes ago, GalD101 said:

this one is 40 MHz

Yes, and we're trying to see a signal the lowest frequency component of which is 100 MHz, so that's pushing it quite a bit beyond its capabilities. I would expect to see something on the Hameg, but I could be mistaken.

Another way to check if the scope is the problem is to change the divider of your CLOCK0 from 6 to 60. That should change the output to a 10 MHz clock, which should be clearly visible on your Hameg.

Edited by reddish
Link to comment
Share on other sites

5 hours ago, reddish said:

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

can you please elaborate on this? What do you mean by generating this stream of bits? how do I actually achieve this?

Link to comment
Share on other sites

Awesome! That's the 100 MHz you synthesized from the 12 MHz, no doubt about it. (In case you have doubt: change the DIV_CLK0 from 6 to 7, and watch as the frequency drops from 100 MHz to 85.71 MHz.)

The reason it is weak and looks wonky is because the pins of your CMOD-board are not really very suitable to carry a signal like that. But there's no denying that's the 100 MHz we've been trying to synthesize. A 100 MHz waveform like that doesn't happen by accident.

Next order of business: state machines!

We will now try to make a little finite state machine that is driven by this newly minted 100 MHz clock, and outputs a repeated pattern of 1-1-1-0-0-0-0-0-0-0 on one of its output flip-flops.

Think back to the image we had before with the green box with registers:

image.png.3004e782808b797eafbac36153e117e9-1.png.e10ba47a67e08e30ded5ec3adf39d65c.png


We'll now make ONE of those boxes. It will be a Verilog module, with its flip-flops clocked by the 100 MHz clock, and the combinatorial logic (in blue) preparing the next state to be put in the flip-flops at the next rising edge of the clock.

Designing such a little state machine can be a bit of an art, but there's a bunch of steps that can lead the way.

First thing to decide, what is the internal state of your new finite state machine (FSM)?

As we agreed before, it is a very good idea to register outputs of your FSM. The thing that will go 1-1-1-0-0-0-0-0-0-0 periodically is an output, let's call it FSM_OUT. It should go through that little train of outputs once every 100 ns; each of the 10 steps will take 10 ns, which corresponds to your 100 MHz clock.

And there is something that must count from 0 to 9 during each period, a counter. After 9, it should become 0 again and repeat the cycle. Let's call this thing COUNTER.

Whenever COUNTER is 0, 1, or 2, FSM_OUT should be 1; otherwise, it should be 0.

Think about this for a bit to agree that these two things are, indeed, the state you will need (the stuff that goes in the green box of the FSM).

The next thing to do is to figure out a way to design the combinatorial logic (the blue box of the FSM, sitting in front of the green box). As an exercise, we will NOT immediately try to implement this in Verilog. Instead, we will implement this in a more regular language, like C, Java, or Python. (Let me know which language you're most fluent in).

As an exercise, let's try to write, in pseudocode, a function that will take the current state of FSM_OUT and COUNTER, and return two values NEW_FSM_OUT and NEW_COUNTER, that determine what value should go in the (green) registers if a clock cycle happens.

So, for example, using Python:
 

def fsm_combinatorial_logic(COUNTER: int, FSM_OUT: int) -> tuple[int, int]:
    # Calculate here values for NEW_COUNTER and NEW_FSM_OUT.
    # Extra challenge: you cannot change the current values of COUNTER and FSM_OUT!

    NEW_COUNTER = ...     # Fill in this
    NEW_FSM_OUT = ...     # And this

    # Return the values.
    return (NEW_COUNTER, NEW_FSM_OUT)


Let's see if you can implement a pure function that captures the combinatorial logic of the FSM we're making.

Don't worry about Verilog for now, just allow yourself to think in a normal high-level language. We'll worry about the translation to Verilog later.



 

Edited by reddish
Link to comment
Share on other sites

def fsm_combinatorial_logic(COUNTER: int, FSM_OUT: int) -> tuple[int, int]:
    # Calculate here values for NEW_COUNTER and NEW_FSM_OUT.
    # Extra challenge: you cannot change the current values of COUNTER and FSM_OUT!

    NEW_COUNTER = (COUNTER + 1) % 9     # Fill in this
    NEW_FSM_OUT = 1 if COUNTER in (0, 1, 2) else 0     # And this

    # Return the values.
    return (NEW_COUNTER, NEW_FSM_OUT)

 

Link to comment
Share on other sites

Do I need to create a new project or should I continue with the same one I created the 100MHz clock with?
Should I create a new file (and module) that instantiates an instance of the clk_wiz_0_clk_wiz module I made just with different parameters for the reset, locked and the two clocks? (in a way that resembles OOP?) and then somehow interact with the memory (in this case the ff?) 

Link to comment
Share on other sites

Is this what I sort of need to do now? I just want to see if I'm on the same page as you.

1. create a clock divider that makes the counter go up by one every 10ns (probably using an always block and figuring out how many 'ticks' of the 100MHz clock correspond to 10 ns)
2. create an always block that on every posedge of the counter (I think? basically a block that will run every time the value of counter changes) will make the necessary changes to the FSM_OUT.
3. Finally assign the values of FSM_OUT to something else
 

Link to comment
Share on other sites

I don't think it will be particularly useful to create a new project. As far as I'm concerned, we'll expand on the current design inside the same project.

You should put each module that you intend to use from another module in its own file. make sure to comment it so you will still understand how it works in a few weeks' time.
If you want, I can review such "self-contained" module files once you are satisfied with them, and I can give feedback on what could be improved. Could be useful, but you're rather pressed for time as it is.
 

There's one software engineering practice that's usual in the HDL world that we won't be doing, which is writing testbenches. That's essentially a test for each of your modules that you can run in software. It's a pretty good idea, but learning to deal with the simulator software in addition to the rest of Vivado would overburden your time budget. There are those who think that writing test-benches is essential, but I'm not one of them, at least for a project of this modest complexity.

Link to comment
Share on other sites

18 minutes ago, GalD101 said:
def fsm_combinatorial_logic(COUNTER: int, FSM_OUT: int) -> tuple[int, int]:
    # Calculate here values for NEW_COUNTER and NEW_FSM_OUT.
    # Extra challenge: you cannot change the current values of COUNTER and FSM_OUT!

    NEW_COUNTER = (COUNTER + 1) % 9     # Fill in this
    NEW_FSM_OUT = 1 if COUNTER in (0, 1, 2) else 0     # And this

    # Return the values.
    return (NEW_COUNTER, NEW_FSM_OUT)

 

was this correct? @reddish

Link to comment
Share on other sites

So the next step is to now create a module that implements the logic you described above? the one with the python code?

 

  

6 minutes ago, reddish said:

which is writing testbenches

when I started reading about verilog I saw a few tutorials about how to create a testbench. It seems like something I would like to learn but you're probably right, it's better to skip it for now.

Link to comment
Share on other sites

4 minutes ago, GalD101 said:

Is this what I sort of need to do now? I just want to see if I'm on the same page as you.

I think it's a good idea to think about what the combinatorial logic will do (in a higher level language than verilog), and worry about the translation to verilog later.

* "create a clock divider that..." -- no, you won't create a clock divider.
* "create an always block that on every posedge of the counter". No, because I don't know what that would mean. Only signals (e.g. clocks) have positive edges, counters don't have edges.
* "Finally assign the values of FSM_OUT to something else". No.

You're getting ahead of yourself. The route we're going is this:

* Write a combinatorial function in a high level language for the period-10 state machine we're now doing.
* Combine this with an always block to get this functionality in Verilog an on your FPGA.
* Then we proceed from there to making an UART, in a few steps, following the same procedure (think about the state and the combinatorial logic, write pseudocode, then proceed to Verilog). As you gain experience with increasingly complicated finite state machines, this process will become easier and easier.

By the way: you already accomplished a great deal today. MMCMs are complex things, and taming them is no mean feat. Feel free to relax for a bit, the tempo is plenty high and you need time to digest new ideas.

Cheers Sidney

 

Link to comment
Share on other sites

4 minutes ago, GalD101 said:

was this correct? @reddish

Almost!

Your modulo 9 (COUNTER % 9) should be a (COUNTER % 10). Your version would produce a period-9 cycle. 0->1->2->3->4->5->6->7->8->0 ...

Also, I'd write NEW_FSM_OUT = 1 if (COUNTER <= 2) else 0, but your version will work, too.

About the faster scope: sure you can hand it back in, but your HAMEG is really too low-end for what you're trying to do; you could hook up to photon detector to it and it could just miss those detections. I'd recommend finding a 100 MHz bandwidth scope at least, given that we'll be running the FPGA at 100 MHz most of the time.


 

Link to comment
Share on other sites

1 minute ago, reddish said:

* "create a clock divider that..." -- no, you won't create a clock divider.

so how will I count 10ns? I think I got confused. Why is changing the clock to operate at 100MHz was necessary in the first place? was it so we will be able to get a better resolution when we will eventually try to capture coincidences? Thank you in advance and thanks a lot for today and thank you in general. this type of thing is quite hard to grasp alone, and it seems I misunderstood some concepts so thank you for the help and dedication

Link to comment
Share on other sites

Hi @GalD101

We've been taming the MMCM because, for your photon coincidence thing, you will need a clock that is faster than 12 MHz. Even 100 MHz is slow for that kind of thing. Apart from that, it's an essential skill to know how to synthesize clocks with a certain desired clock frequency when using an FPGA.

> so how will I count 10ns?

The CLK_OUT_100MHz will do that for you. And you've made that already, using the MMCM.

Every 10 ns, the CLK_OUT_100MHz will have a rising clock edge ("posedge"), at which (near)instant the registers in your design will be updated with whatever value the combinatorial logic has prepared for them. So the code inside the always @ (posedge CLK_100MHz) gets executed at those times, once every 10 ns.

> I think I got confused.

This is normal, you're doing fine (more than fine, in fact). Programming in a hardware description language is pretty strange when you're used to "regular" programming languages.

As we progress, more things will start to make sense. Re-read the earlier posts in this thread sometime, you will already be in a position to absorb more of the details I wrote that you may have glossed over the first time.

There's no royal road to FPGAs; it's complicated and weird. But the dividend is also there: master this, and you will be able to implement functionality that would require a 10,000 USD device on a 100 USD development board. Also, once you get the hang of it (and you may not believe me at this time): it's fun.

 

Link to comment
Share on other sites

Hio @GalD101

As a shortcut, I will now post here my version of the "30% duty cycle output" design. It comprises three files.

The "toplevel" file instantiates the 100 MHz clock synthesizer and the FSM instance. Those two things are each defined in their own file.

A few remarks:

First, note that I am not an experienced Verilog programmer. It could well be that my style is a bit ... idiosyncratic.

Second, in the "my_first_fsm" module, I prepend the names of registers with "r_". I think that's a useful convention for the time being.

Third, remember your little piece of Python code?

Now see the "my_first_fsm.v" and be amazed. It's there, almost verbatim.


At this point, I want to ask you to do the following things:

(1) Compare what I did to what you're doing. Find the differences and commonalities.
(2) Run this design (make sure that the names of the top-level ports correspond to your XDC file) and observe on the scope the marvels of a 10 MHz, 30% duty-cycle signal on a scope.
(3) While looking at the code of "my_first_fsm" module, re-read the stuff I wrote about "combinatorial" logic feeding into "registers". Make sure you see how all that abstract blah-blah translates into code that is, well, pretty easy to read, I hope. Right?

Make really sure you fully understand how the my_first_fsm works, and why some things are called "wires" while other things are called "registers".

Once you're confident you get it, here's a little exercise for you:

Given that you are using a 100 MHz clock, alter the 'my_first_fsm' code so that it produces a signal that toggles at a rate of approximately 115200 times per second, and show the resulting image on a scope.
 

clocksynth.v my_first_fsm.v toplevel.v

Edited by reddish
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...