Author Topic: Why does this dds code fail (Solved)  (Read 6339 times)

0 Members and 1 Guest are viewing this topic.

Offline pcprogrammerTopic starter

  • Super Contributor
  • ***
  • Posts: 4323
  • Country: nl
Re: Why does this dds code fail
« Reply #50 on: April 03, 2022, 05:21:14 am »
using a case statement or an if else structure seems to be only possible within always blocks and then can't assign data to a wire. This means I would have to make another register to clock the selected data into and use that register
Sounds like yo have picked up Verilog on a as-needs basis rather than learning it from scratch (have met several people who have done this). Recommendation, STOP! HDL coding is nothing like other software even if it looks the same. If you jump in without understanding the basics, and learning the strict terminology, it will be very confusing.

Sadly the freely available references/guides/courses for Verilog are not as numerous or quality as VHDL, but there are some out there that cover these sorts of fundamentals:
https://verilogguide.readthedocs.io/en/latest/verilog/procedure.html

Just because Verilog calls it a register doesn't mean that becomes a register in implementation, confusing!
What can I say, nowadays I'm finding it hard to plow through documentation without a direct practical use case, and then even keeping focus is tough. With chronic fatigue syndrome things slow down. But still like to hobby.

I choose verilog over vhdl based on it being closer to C syntax. You are right that there is not a lot about verilog on the net, but I did find a couple:

http://www.asic-world.com/verilog/index.html
https://www.chipverify.com/verilog/verilog-tutorial

Sadly most the given examples do not provide the information I'm looking for, and learning from professionals helps a lot.
It does require another way of thinking doing hardware instead of software, which is tricky after so many years of just doing software.

Offline pcprogrammerTopic starter

  • Super Contributor
  • ***
  • Posts: 4323
  • Country: nl
Re: Why does this dds code fail
« Reply #51 on: April 03, 2022, 06:01:23 am »
It's pen and paper time...
Please visually illustrate to me what you want to achieve with your waveform.
I think you are going about things in a completely backwards manner.
I need to see the function output you are trying to generate.
Also, do you have a modelsim testbench of your code?

The generator I have working now only gives a saw tooth at the output of the DAC. I like to be able to select different wave forms via a control register. I tested the other wave forms by hard coding the assignments and they work.

Eventually it should also have an arbitrary lookup table based output, which is not that hard to do. Setup block ram and address it with part of the signal phase counter.

The problem lies in the multiplexing between the different data. So I'm looking for the proper way to do that. I have drawn a simple schematic like you asked.

I tried it with a case statement but got errors about not being able to write to a wire, which in a way makes sense.

Code: [Select]
reg [2:0] waveform_control = 0;
wire [13:0] dac_signal_data;

always@(waveform_control)
case(waveform_control)
default: dac_signal_data = signal_phase[47:34];
3'h1:  dac_signal_data = ~signal_phase[47:34];
3'h2: dac_signal_data = signal_phase[47] ? 14'h3FFF : 14'h0;
endcase

Just tried it with a reg and it does work, but the FMAX takes a hit. Drops down from ~180MHz to ~159MHz

Code: [Select]
reg [2:0] waveform_control = 0;
reg [13:0] dac_signal_data;

always@(waveform_control or signal_phase[47:34])
case(waveform_control)
default: dac_signal_data = signal_phase[47:34];
3'h1:  dac_signal_data = ~signal_phase[47:34];
3'h2: dac_signal_data = signal_phase[47] ? 14'h3FFF : 14'h0;
endcase

So is it better practice to also use the main clock (125MHz) for this always block and have the data clock into an actual DFF.

Offline BrianHG

  • Super Contributor
  • ***
  • Posts: 8089
  • Country: ca
Re: Why does this dds code fail
« Reply #52 on: April 03, 2022, 04:23:15 pm »
It's pen and paper time...
Please visually illustrate to me what you want to achieve with your waveform.
I think you are going about things in a completely backwards manner.
I need to see the function output you are trying to generate.
Also, do you have a modelsim testbench of your code?

The generator I have working now only gives a saw tooth at the output of the DAC. I like to be able to select different wave forms via a control register. I tested the other wave forms by hard coding the assignments and they work.

Eventually it should also have an arbitrary lookup table based output, which is not that hard to do. Setup block ram and address it with part of the signal phase counter.

The problem lies in the multiplexing between the different data. So I'm looking for the proper way to do that. I have drawn a simple schematic like you asked.

I tried it with a case statement but got errors about not being able to write to a wire, which in a way makes sense.

Code: [Select]
reg [2:0] waveform_control = 0;
wire [13:0] dac_signal_data;

always@(waveform_control)
case(waveform_control)
default: dac_signal_data = signal_phase[47:34];
3'h1:  dac_signal_data = ~signal_phase[47:34];
3'h2: dac_signal_data = signal_phase[47] ? 14'h3FFF : 14'h0;
endcase

Just tried it with a reg and it does work, but the FMAX takes a hit. Drops down from ~180MHz to ~159MHz

Code: [Select]
reg [2:0] waveform_control = 0;
reg [13:0] dac_signal_data;

always@(waveform_control or signal_phase[47:34])
case(waveform_control)
default: dac_signal_data = signal_phase[47:34];
3'h1:  dac_signal_data = ~signal_phase[47:34];
3'h2: dac_signal_data = signal_phase[47] ? 14'h3FFF : 14'h0;
endcase

So is it better practice to also use the main clock (125MHz) for this always block and have the data clock into an actual DFF.

Your waveform control should be an input from your MCU control regs.
dac_signal_data should be your output reg going to the IO pins, or DDR buffers.

Code: [Select]
always@(posedge system_CLK_125MHz) begin
case(waveform_control)
default: dac_signal_data <= signal_phase[47:34];
3'h1:  dac_signal_data <= ~signal_phase[47:34];
3'h2: dac_signal_data <= signal_phase[47] ? 14'h3FFF : 14'h0;
endcase
end

Your missing the saw and sine.
To get the best quality sine for the least amount of memory, it is a little tricky as you would only be storing 1/4 a waveform and realtime compute the 3 other quadrants.  See how much realistic memory is available to you and use a ^2 size with a 16 bit output as your FPGA probably reserves memory in blocks which are 1/2/4/8/16 bits wide.
« Last Edit: April 03, 2022, 04:27:37 pm by BrianHG »
 

Offline pcprogrammerTopic starter

  • Super Contributor
  • ***
  • Posts: 4323
  • Country: nl
Re: Why does this dds code fail
« Reply #53 on: April 03, 2022, 04:51:10 pm »
Your missing the saw and sine.
To get the best quality sine for the least amount of memory, it is a little tricky as you would only be storing 1/4 a waveform and realtime compute the 3 other quadrants.  See how much realistic memory is available to you and use a ^2 size with a 16 bit output as your FPGA probably reserves memory in blocks which are 1/2/4/8/16 bits wide.

The code was just a sample to make the point. I still have to get used to do everything on the "master" clock. Already tried the always@(posedge clk_125MHz) and it did raise the FMAX back up to ~180MHz, so that also answered the question I guess.

I think the memory setup is similar to the Cyclone IV. Already have some samples of that in the FNIRSI project of morris6, and found several websites about the quarter sine within FPGA. Also have experience with it in C, so don't see a problem there.

The, what you call, saw is triangle in my mind, and for my "pulse width" setup it also needs to be done in 4 quadrants. Have to make some setup with the upper two bits of the signal phase to make that work.

The project is taking proper shape now. Just a couple of steps and then back to writing C code to allow controlling of the two signals.

Again thanks for your help Brian.
Cheers,
Peter

Offline Someone

  • Super Contributor
  • ***
  • Posts: 4959
  • Country: au
    • send complaints here
Re: Why does this dds code fail
« Reply #54 on: April 04, 2022, 04:03:37 am »
using a case statement or an if else structure seems to be only possible within always blocks and then can't assign data to a wire. This means I would have to make another register to clock the selected data into and use that register
Sounds like yo have picked up Verilog on a as-needs basis rather than learning it from scratch (have met several people who have done this). Recommendation, STOP! HDL coding is nothing like other software even if it looks the same. If you jump in without understanding the basics, and learning the strict terminology, it will be very confusing.

Sadly the freely available references/guides/courses for Verilog are not as numerous or quality as VHDL, but there are some out there that cover these sorts of fundamentals:
https://verilogguide.readthedocs.io/en/latest/verilog/procedure.html

Just because Verilog calls it a register doesn't mean that becomes a register in implementation, confusing!
What can I say, nowadays I'm finding it hard to plow through documentation without a direct practical use case, and then even keeping focus is tough. With chronic fatigue syndrome things slow down. But still like to hobby.

I choose verilog over vhdl based on it being closer to C syntax. You are right that there is not a lot about verilog on the net, but I did find a couple:

http://www.asic-world.com/verilog/index.html
https://www.chipverify.com/verilog/verilog-tutorial

Sadly most the given examples do not provide the information I'm looking for, and learning from professionals helps a lot.
It does require another way of thinking doing hardware instead of software, which is tricky after so many years of just doing software.
The syntax might have some C style layout/elements but how that implements is entirely different. I was pointing out (with reference to documentation) that your idea of always block = register is incorrect. Always block is an abstraction that can mean many different things, having code inside an always block does not mean there are registers (it can be combinatorial/asynchronous). Having a Verilog object declared as a register does not mean it will be implemented as a register, it could be nothing more than a passive signal "wire" (but not interchangeable with a Verilog wire), or it could be a latch, or something else like an sram cell. A Verilog register could be optimized away into the middle of blob of logic so that it cant even be observed or uniquely identified in the implementation. It is an abstraction.

There isnt one way to describe a multiplexer, I can think of 4 common multiplexer descriptions in Verilog/VHDL, all able to implement a combinatorial or clocked/sequential/registered multiplexer. Which one to use in any specific situation is dependent on the structure and style of the code/system around it. You'll keep running into mental blocks and these sorts of problems if you just copypasta blocks of code which although they do the larger function you are thinking of, you're not understanding why they are doing it. How to use an always block is where you need to start (as evidenced from your follow on code examples).
 

Offline pcprogrammerTopic starter

  • Super Contributor
  • ***
  • Posts: 4323
  • Country: nl
Re: Why does this dds code fail
« Reply #55 on: April 04, 2022, 03:44:43 pm »
Apart from the sine wave the other signals are implemented.

Removed the MCU read stuff since I don't need it for this project.

Attached are some screen captures of the Hantek DSO with signals generated with this hardware.

Edit: also attached a modelsim project for simulating the triangle setup.
« Last Edit: April 04, 2022, 03:48:06 pm by pcprogrammer »
 

Offline pcprogrammerTopic starter

  • Super Contributor
  • ***
  • Posts: 4323
  • Country: nl
Re: Why does this dds code fail
« Reply #56 on: April 07, 2022, 09:24:39 am »
Implemented the sine wave with a dual port rom to serve the two channels.

First direct sine creation attempt broke the FMAX, but by implementing it with a pipeline structure found on the internet FMAX is back up to ~180MHz and the signal looks good.

Had fun and learned a lot from doing this project.

 :-+ for BrianHG.


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf