Recently, I saw a signal generator project on an AVR microcontroller. The principle of generation is DDS , based on the Jesper library, the maximum frequency is 65534 Hz ​​(and up to 8 MHz HS output with a square wave). And then I thought that the generator is an excellent task, where the FPGA can show itself in the best possible way. As a sports interest, I decided to repeat the project on the FPGA, while meeting the deadlines on two days off, and the parameters to get are not strictly defined, but the maximum possible. What came out of this can be found under the cut
Day zero
Before the weekend came, I had a little time to think about the implementation. To simplify my task, I decided to make the generator not as a separate device with buttons and an LCD screen, but as a device that connects to a PC via USB. For this, I have a USB2RS232 board . The driver board does not require (CDC), so I think it will work under Linux (for some, this is important). Also, I will not hide that I already worked with the reception of messages on RS232. I will take modules for work with RS232 ready with opencores.com .
To generate a sinusoidal signal, you will need a DAC. I chose the type of DAC, as in the initial project - R2R on 8 bits. It will allow to work at high frequencies, of the order of megahertz. I am convinced that the FPGA must cope with this
About what to write a program for data transmission through the COM port, I was thinking. On the one hand, you can write on Delphi7, the experience of writing such a program already exists, besides, the size of the executable file will not be large. I also tried to sketch something to work with Serial in the form of a java script in the html page, but more or less earned only through the Chrome serial API, but for this you need to install the plugin ... in general, too, disappears. As an innovation, I tried PyQt5 for myself, but when distributing such a project, you need to drag a bunch of libraries. Having tried to build a PyQt project in an exe file, it turned out more than 10 MB. That is, there will be nothing better than an application written in c ++ \ Qt5. I should also take into account that I don’t have any development experience in python, but I do have Qt5. Therefore, the choice fell on Qt5. From the fifth version there appeared a module for working with serial and I already worked with it. And the application on Qt5 can be transferred to Linux and Mac (for some, this is important), and from version 5.2, applications on QWidgets can be transferred even to a smartphone!
What else is needed? Naturally board with FPGA. I have two of them (Cyclone iv EP4CE10E22C8N for 10 thousand cells, and Cyclone ii EP2C5 for 5 thousand cells). I will choose the one on the left, solely because of the more convenient connector. In terms of volume, the project does not intend to be large, so it fits in either of the two. In terms of speed, they do not differ. Both boards have “on board” 50 MHz generators, and inside the FPGA there is a PLL , with which I can increase the frequency to the planned 200 MHz. ')
The first day
Due to the fact that I already did the DDS module in my synthesizer project, I immediately took up the soldering iron and began to solder the DAC on the resistors. He took a mockup. Installation did with the use of cheating . The only change that affected the technology was that I abandoned the acid F38N for tinning the racks in favor of the TT indicator flux gel . The essence of the technology is simple: I solder racks to the PCB, solder resistors on them from the PCB side. Missing connections are performed by wrap. Still, the racks are convenient because I can insert them directly into the FPGA board.
Unfortunately, at home there were no resistors 1 and 2 kilooma. There was no time to go to the store. I had to sacrifice one of their rules, and remove the resistors from the old unnecessary board. They used resistors 15K and 30K. The result is such a Frankenstein:
Then I launched Quartus, created a project.
After creating the project, you need to set the target device: Assigments menu -> Device
Further, in the same place I press the button “Device and Pin options” because some pins are configured so that they will not work. I configure everything as "Use as regular I / O"
In the project, I "nahadrkodil" unmanaged main module DDS at a fixed frequency.
After that, I clicked “Start Compilation” so that the development environment wondered what our input / output lines are in the main project module and what physical PIN's they are connected to. You can connect to almost any. After compilation, assign the appeared lines to the real PIN of the FPGA chip:
Assigments menu item -> Pin Planner
On the HS_OUT, key0 and key1 lines, I ask you not to pay attention yet, they appear in the project afterwards, but I didn’t manage to make the screen at the very beginning.
In principle, it is enough to “register” only PIN_nn in the Location column, and the remaining parameters (I / O standart, Current Strench and Slew Rate) can be left by default, or you can choose the same options that are suggested by default (default) so that there is no warning 's.
How to find out what PIN corresponds to the slot number on the board?
Socket pin numbers are on board
And the FPGA pins, to which the connector pins are connected, are described in the documentation that comes with the FPGA board.
After the pins are assigned, I compile the project again and flash it with the help of a USB programmer. If the drivers for the USB Byte blaster programmer are not installed, then indicate to Windows that they are located in the folder where you installed Quartus. Then she will find herself.
Connect the programmer to the JTAG connector. A menu item for programming "Tools -> Programmer" (or click the icon on the toolbar). The “Start” button, joyful “Success” and the firmware are already inside the FPGA and are already working. Just do not turn off the FPGA, otherwise she will forget everything.
Tools -> Programmer
The DAC is connected to the FPGA board connector. I connect the oscilloscope S1-112A to the DAC output. As a result, the “saw” should turn out because the high bit of the word DDS of the battery of the phase is output on 8 bits. And it always increases until it overflows.
Some 1.5 hours and for a frequency of 1000 Hz, I see the following waveform:
I want to note that the "saw" in the middle has a small fracture. It is due to the fact that the resistors have a spread of values.
Another important point that needed to be clarified is the maximum possible frequency with which the DDS generator will work. With correctly configured TimeQuest parameters, after compilation in the Compilation Report, you can see that the speed of the circuit is above 200 MHz with a margin. And this means that I will multiply the frequency of the 50 MHz oscillator with the help of PLL by 4. I will increase the value of the battery of the DDS phase with a frequency of 200 MHz. The final frequency range that can be obtained in our conditions is 0 - 100 MHz. Frequency setting accuracy:
200000000 (clk) / 2^32 (DDS) = 0,047
That is, it is better than ~ 0.05 Hz. I consider the accuracy in the Hertz fraction for a generator with such a range of operating frequencies (0 ... 100 MHz) to be sufficient. If someone needs to increase accuracy, then for this you can increase the DDS bit (do not forget to check the TimeQuest Timing Analyzer, that the speed of the logic is within CLK = 200 MHz, because it is an accumulator), or simply reduce the clock frequency, if such A wide range of frequencies is required.
TimeQuest Timing Analyzer
After I saw the “saw” on the screen, family matters forced me to go to the country (the same day off). There I mowed, cooked, grilled kebabs and did not know about the surprise that I was waiting for in the evening. Already closer to the night before bedtime, I decided to look at the waveform for other frequencies.
For a frequency of 100 kHz
For a frequency of 250 kHz
For the frequency of 500 KHz
For frequency 1 MHz
I will not hide that the shape of the signals has upset me, especially at 1 MHz (pathetic, useless megahertz!). I planned to get the frequencies of several other orders. After reading about the R2R DAC, the cause of the problem became clear - stray capacitances. Therefore, the plans for the next day, it was decided to make a DAC on 100 and 200 Ohm resistors, which I have in stock, and leave this DAC for future developments that do not require working at such high frequencies, because the smoothness of the saw also has its plus.
Second day
Due to the fact that it was interesting how the DAC will work on resistors of 100 and 200 Ohms, I immediately took up the soldering iron. This time, the DAC turned out to be more accurate, and it took less time to assemble it.
We put the DAC on the FPGA board and connect it to the oscilloscope
Checking 1 MHz - IN! It is quite another matter!
Saw 10 MHz
Saw 25 MHz
The shape of the saw at 10 MHz is still similar to the correct one. But at 25 MHz, it is already quite "not beautiful." However, C1-112a has a bandwidth of 10 MHz, so in this case the reason may already be in the oscilloscope.
In principle, this issue with the DAC can be considered closed. Now remove the high-speed waveform. To do this, we will output the most significant bit to a separate PIN FPGA. The data for this line will be taken from the high bit of the DDS battery.
assign hs_out = accumulator[31];
1 MHz square wave
5 MHz meander
Meander 25 MHz
50 MHz meander is almost not visible
But I think that the output of the FPGA would be worth the load on the resistance. Perhaps the fronts would be cooler.
Sine is done on the table. The size of the table is 256 values ​​of 8 bits each. It would be possible to take more, but I already had a ready mif file. Using the wizard, create a ROM element with the sine table data from the mif file.
Creating a ROM - Tools -> Mega Wizard Plugin manager
Select 1 port ROM and set the name for the module.
Agree
Here we also agree
Using browse we find our mif file with sine table
Here, too, do not change anything
We uncheck the sine_rom_bb.v module - it is not needed. Next finish. Quartus will ask you to add a module to the project - we agree. After that, the module can be used just like any other module in Verilog.
The upper 8 bits of the DDS battery word will be used as the ROM address, and the data output will be the sine value.
Code
//sine rom wire [7:0] sine_out; sine_rom sine1(.clock(clk200M), .address(accumulator[31:31-7]), .q(sine_out));
The sine waveform at different frequencies looks ... the same.
If desired, you can consider the problems of the DAC associated with the scatter of resistors:
Well, this weekend is over. But not yet written software for PC control. I am compelled to state the fact that I did not meet the planned deadlines.
Third day
There is very little time, so we are writing a program in haste (in the best traditions). In some places, in order to reduce the number of letters and the convenience of entering information from the keyboard, an event filter by the name of the widget is used. Please understand and forgive.
Interface
GitHub source code. There is also an application already compiled under windows.
The code is as simple as 5 kopecks. Among other things, you need to add the serialport module to the project file.
In a hurry, we finish receiving data on the UART. To receive messages on the UART you need to put a couple of modules. One Baud generator, the second - the receiver. In order for the receiver to work at 115200, you need to make some calculations, assuming that the main clock frequency is 200 MHz.
Then I put all this in a separate module, which gives only the number of the waveform and the increment value to the register of the battery of the DDS phase.
When the uart_rx module received a byte of information, it puts the uart_data_ready line into one unit. At this time on the line uart_command is received bytes. To receive a message I am writing a steytmashin.
always @(posedge clk200M) begin accumulator <= accumulator + adder_value; end
From the highest part of the value of the battery phase, we obtain the remaining waveforms. And depending on the selected form - connect it to the output.
I was almost not surprised that it immediately worked. The only mistake I found was in the calculations: I divided the desired frequency into CLK, then by two more, then multiplied by the battery capacity. But this is not necessary, because we get 1 period when the value of the battery changes from 0 to MAX. It is necessary to divide an additional 2 only if taking the most significant bit of the battery frequency as the output of the meander (in this case the frequency is 2 times lower). But getting the meander I redid.
Day four
It can include the time spent on each day in the design of the article.
We proceed to check. First with an oscilloscope.
At radio frequencies from 28 to 100 MHz, I decided to listen to the generator using an SDR receiver, placing the antenna next to the board.
findings
As is often the case in IT, there was an error of 2–2.5 times with the time estimate. The goal has been achieved: a generator up to 100 MHz is assembled on the knee. However, so that this work could be called a full-fledged generator, it will take more work. Therefore, there are great prospects for development. Due to the deadlines, I did not add what I could in principle: 1) a noise generator; 2) a wave generator that the user draws himself; 3) digital sequence generator. There is no amplitude and offset adjustment in the generator.
227 cells out of 10,000 were used. A list of what else can be added to the project:
Expand the bit width of the DAC
Increase the number of outputs with generated signals
Apply the DAC chip in a higher bit depth
Implement amplitude and offset control
Add controls, LCD screen, for portability
Add noise generator and other simple waveforms
Add the ability to download arbitrary waveforms
I think that anyone can do this on their own by expanding the project with the necessary functionality. Add is easier than from scratch. The format of the control command is very simple, so the generator can be controlled from a microcontroller.