Python Script to Verilog RTL - Page 1
Forum section: electronics / general

author: blackblade
author_rank: Newbie
timestamp: June 30, 2022, 06:51:27 am
content: Can anyone give me an idea on how to implement an efficient Verilog HDL of the below Python code. I'm not asking for code or algorithm, just want someone to point to proper sources.I have posted only that part which needs to be converted to RTL.Code:# Constant defADC_SR = 1966.08e6ADC_SAMPLE_SIZE = 65536READOUT_SIGNAL_FREQUENCY = 1e6# Data loading, the data is an array of signed short with size 65536with open(FPATH / "sig", "rb") as r:sig = np.frombuffer(r.read(), dtype="i2", count=ADC_SAMPLE_SIZE)# Processing the zero stateI = 0Q = 0# We iterate over the ADC signal data and multiply it with a cosine/sine wave of the same frequencyfor i in range(ADC_SAMPLE_SIZE):t = i / ADC_SR# Then, we sum the results respectivelyI += sig * np.cos(2 * np.pi * READOUT_SIGNAL_FREQUENCY * t)Q += sig * np.sin(2 * np.pi * READOUT_SIGNAL_FREQUENCY * t)I have the RTL block which can generate sine and cosine wave. I'm looking more at the addition and multiplication in for loop block which loops for 65536 times. Seems like an integration to me which can be implemented in some way in Verilog.Thanks.

author: agehall
author_rank: Frequent Contributor
timestamp: June 30, 2022, 07:16:07 am
content: I would say thathttps://www.chipverify.com/systemverilog/systemverilog-tutorialcontains most of the information you would need.

author: hamster_nz
author_rank: Super Contributor
timestamp: June 30, 2022, 07:53:55 am
content: You would need to restructure it a little... to something like this (in no particular HDL)Code:inputs: data_in and data_in_enableoutputs data_out_i, data_out_q and data_out_enableon the clock edgeif data_in_enable {if count == count_max {data_out_enable = truecount = 0data_out_i = i + data_in * cos_tabledata_out_q = q + data_in * sin_tableq = 0i = 0} else {i = data_in * cos_tableq = data_in * sin_tablecount = count + 1data_out_enable = false}} else {data_out_enable = false}You might need to replace cos_table and sin_table that actually calculates the required values.. (e.g. a NCO driving a CORDIC SIN+COS or a lookup table with interpolation)

author: Someone
author_rank: Super Contributor
timestamp: June 30, 2022, 07:56:58 am
content: Quote from: blackblade on June 30, 2022, 06:51:27 amVerilog (or other HDL) is just a language, it can run pretty much as you wrote it:Code:module blackblade;real ADC_SR = 1966.08e6;real READOUT_SIGNAL_FREQUENCY = 1e6;real pi = 3.141592653589793238462643383279502884197169399375;integer ADC_SAMPLE_SIZE = 65536;integer i;real I = 0.0;initial beginfor (i=0;i<ADC_SAMPLE_SIZE;i++) beginI += $sin(2.0 * pi * READOUT_SIGNAL_FREQUENCY * i/ADC_SR);end$finish;endendmoduleThe code is not the hard part. How it interfaces with other things, what the target hardware is, what the system constraints/specifications are, etc etc all those details are needed to write what you actually need.

author: laugensalm
author_rank: Regular Contributor
timestamp: June 30, 2022, 04:46:42 pm
content: There are various Python HDL approaches that would do the job, but before you dive into that you'd have to get familiar with hardware development methods and you'll have to rewrite your model in a specific HDL style. Your case is one of those typical HLS (high level synthesis) scenarios, where you have a software based model ready, and the next step would be to bake it into hardware. But that's not trivial, and the tools can't just guess and infer the right hardware accelerator for your code snippet. You'll have to deal with pipelines, how to unroll a `for ..` loop, choose the proper fixpoint arithmetics, etc. Once you've got a clocked 'true' hardware model ready though, Python can be very much your friend to verify the hardware behaviour against your software model and in general help to get things under control, down to verification of the synthesized results.

author: Someone
author_rank: Super Contributor
timestamp: June 30, 2022, 11:14:41 pm
content: Quote from: laugensalm on June 30, 2022, 04:46:42 pmThe software model isnt even "ready", ask for floats? HLS will go right a head and implement floats.....cue ignorant researcher complaining/publishing about how FPGAs are so inefficient and bad at doing what their general purpose language running on a CPU did so much quicker.....The algorithm needs to be designed for purpose, a massive preloaded array is huge red flag that its not going to translate well to an FPGA implementation. Streams are still a novelty/unusual construct in the broader world of programming, but basically essential to architecting FPGA algorithms.

author: laugensalm
author_rank: Regular Contributor
timestamp: July 01, 2022, 09:31:55 am
content: Quote from: Someone on June 30, 2022, 11:14:41 pmNot necessarily, for a trivial MAC (multiply-accumulate) scenario, the necessary precision can be derived from the parameters and calculation and the fixed point (FP) format is at least in my case (Python 'HLS' approach) automatically determined, as the input data is 16 bit integer. BUT: there are plenty of caveats and possible mismatches between the float and FP model, especially when it comes to error propagation.Therefore one should avoid float for the calculation pipeline and use a fixed point bit vector data type in Python in the first place for a bit accurate model.Indeed many academics not familiar with HDL seem to spend a lot of time on C -> HDL HLS approaches that are IMHO broken by design. It is mandatory to have a hardware based concept on what you're trying to achieve, as answering to:How does the data get in (how is it buffered, at which frequency)Where does the resulting data go (at which frequency)What's the allowed latencyThis after all determines your (possibly pipelined) architecture of MAC DSP elements. My dogma these days: Write explicit HDL ('keep it simple, stupid) where possible instead using HLS, check what FPGA primitives the tools infer to, simulate. For more complex projects, Python HLS capable HDL classes can help, but you need to evaluate that extra investment of time carefully (it can pay off in the big picture WRT verification).

author: radiolistener
author_rank: Super Contributor
timestamp: July 05, 2022, 04:17:02 am
content: Quote from: blackblade on June 30, 2022, 06:51:27 amIt looks like classic frequency shift with sine/cosine multiplication. It can be done with cordic algorithm in verilog.Here is a ready to use verilog module source code with cordic sin/cos multiply implementation, it doing exactly what you want:https://github.com/TAPR/OpenHPSDR-Firmware/blob/master/Protocol%201/Mercury/Source/Mercury_V3.3/cordic.vYou can use it in the following way:Code:logic [15:0] sig;logic [21:0] out_i;logic [21:0] out_q;logic [63:0] phase_word; // see description belowcordic cordic_i(.clock(clock), // your ADC clock.frequency(phase_word[56:25]), // B57 -> B32 number since R is always >= 0.in_data(sig), // your ADC sample.out_data_I(out_i), // out I.out_data_Q(out_q) // out Q);phase_word value here is a phase frequency (in oscillator clock units), it depends on your clock frequency and you can calculate it in the following way:Code:logic [31:0] freq_hz;// M2 = 2^57 / OSCILLATOR_CLOCK_HZlocalparam M2 = (64'd1 << 57) / OSCILLATOR_CLOCK_HZ;// M3 = 2^24localparam M3 = 32'd16777216;assign phase_word = freq_hz * M2 + M3;where:OSCILLATOR_CLOCK_HZ - your clock frequency in Hz (in your case ADC_SR)freq_hz - shift frequency in Hz (in your case READOUT_SIGNAL_FREQUENCY)you can find it's calculation this source file:https://github.com/TAPR/OpenHPSDR-Firmware/blob/master/Protocol%201/Mercury/Source/Mercury_V3.3/Mercury.vin your case clock = ADC_SR = 1966080000 Hz and your freq_hz = READOUT_SIGNAL_FREQUENCY = 1000000 Hz, so your phase_word calculation will be the following:Code:localparam M2 = (64'd1 << 57) / 64'd1966080000;localparam M3 = 32'd16777216;assign phase_word = 32'd1000000 * M2 + M3;

author: radiolistener
author_rank: Super Contributor
timestamp: July 05, 2022, 05:21:46 am
content: Quote from: blackblade on June 30, 2022, 06:51:27 amif you already have NCO module which produces sine and cosine wave, then just use multiply operation:Code:logic clock; // ADC clocklogic [15:0] sig; // ADC samplelogic [15:0] nco_sin;logic [15:0] nco_cos;logic [31:0] out_i;logic [31:0] out_q;always @(posedge clock) beginout_i <= nco_cos * sig;out_q <= nco_sin * sig;endQuote from: blackblade on June 30, 2022, 06:51:27 amthere is no need for a loop, because FPGA working in a different way than usual programming language. It just get and process ADC sample on every ADC clock pulse in realtime with no need to store samples in the buffer. And your HDL code needs to describe on how to do it.In the code above all statements in always block are executed simultaneously (in parallel) on positive edge of a clock signal. There is no one by one sequence like in a usual programming language. This is very important difference. And this is why there is no plain conversion between python and verilog language. HDL language is not CPU language, this is hardware definition language. It has different nature. It doesn't describes program, it describes circuit.