Author Topic: Python Script to Verilog RTL  (Read 956 times)

0 Members and 1 Guest are viewing this topic.

Offline blackblade

  • Contributor
  • Posts: 6
  • Country: in
Python Script to Verilog RTL
« on: June 30, 2022, 06:51:27 am »
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: [Select]
# Constant def
ADC_SR = 1966.08e6
ADC_SAMPLE_SIZE = 65536
READOUT_SIGNAL_FREQUENCY = 1e6

# Data loading, the data is an array of signed short with size 65536
with open(FPATH / "sig", "rb") as r:
    sig = np.frombuffer(r.read(), dtype="i2", count=ADC_SAMPLE_SIZE)

# Processing the zero state
I = 0
Q = 0

# We iterate over the ADC signal data and multiply it with a cosine/sine wave of the same frequency
for i in range(ADC_SAMPLE_SIZE):
    t = i / ADC_SR

    # Then, we sum the results respectively
    I += sig[i] * np.cos(2 * np.pi * READOUT_SIGNAL_FREQUENCY * t)
    Q += sig[i] * 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.

 

Offline agehall

  • Frequent Contributor
  • **
  • Posts: 356
  • Country: se
Re: Python Script to Verilog RTL
« Reply #1 on: June 30, 2022, 07:16:07 am »
I would say that https://www.chipverify.com/systemverilog/systemverilog-tutorial contains most of the information you would need.
 
The following users thanked this post: blackblade

Offline hamster_nz

  • Super Contributor
  • ***
  • Posts: 2759
  • Country: nz
Re: Python Script to Verilog RTL
« Reply #2 on: June 30, 2022, 07:53:55 am »
You would need to restructure it a little... to something like this (in no particular HDL)

Code: [Select]
inputs: data_in and data_in_enable

outputs data_out_i, data_out_q and data_out_enable

on the clock edge
    if data_in_enable {
        if count == count_max {
            data_out_enable = true
            count = 0
            data_out_i = i + data_in * cos_table[count]
            data_out_q = q + data_in * sin_table[count]
            q = 0
            i = 0
        } else {
            i = data_in * cos_table[count]
            q = data_in * sin_table[count]
            count = count + 1
           data_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)
Gaze not into the abyss, lest you become recognized as an abyss domain expert, and they expect you keep gazing into the damn thing.
 
The following users thanked this post: blackblade

Online Someone

  • Super Contributor
  • ***
  • Posts: 3709
  • Country: au
    • send complaints here
Re: Python Script to Verilog RTL
« Reply #3 on: June 30, 2022, 07:56:58 am »
implement an efficient Verilog HDL of the below Python code.
Verilog (or other HDL) is just a language, it can run pretty much as you wrote it:

Code: [Select]
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 begin
    for (i=0;i<ADC_SAMPLE_SIZE;i++) begin
        I += $sin(2.0 * pi * READOUT_SIGNAL_FREQUENCY * i/ADC_SR);
    end
    $finish;
end
endmodule

The 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.
 
The following users thanked this post: blackblade

Offline laugensalm

  • Regular Contributor
  • *
  • Posts: 99
  • Country: ch
Re: Python Script to Verilog RTL
« Reply #4 on: June 30, 2022, 04:46:42 pm »
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.
 
The following users thanked this post: blackblade

Online Someone

  • Super Contributor
  • ***
  • Posts: 3709
  • Country: au
    • send complaints here
Re: Python Script to Verilog RTL
« Reply #5 on: June 30, 2022, 11:14:41 pm »
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.
The 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.
 
The following users thanked this post: blackblade

Offline laugensalm

  • Regular Contributor
  • *
  • Posts: 99
  • Country: ch
Re: Python Script to Verilog RTL
« Reply #6 on: July 01, 2022, 09:31:55 am »
The software model isnt even "ready", ask for floats? HLS will go right a head and implement floats.....

Not 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 latency

This 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).
 
The following users thanked this post: blackblade

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 2274
  • Country: ua
Re: Python Script to Verilog RTL
« Reply #7 on: July 05, 2022, 04:17:02 am »
I have posted only that part which needs to be converted to RTL.
Code: [Select]
# We iterate over the ADC signal data and multiply it with a cosine/sine wave of the same frequency
for i in range(ADC_SAMPLE_SIZE):
    t = i / ADC_SR

    # Then, we sum the results respectively
    I += sig[i] * np.cos(2 * np.pi * READOUT_SIGNAL_FREQUENCY * t)
    Q += sig[i] * np.sin(2 * np.pi * READOUT_SIGNAL_FREQUENCY * t)

It 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.v

You can use it in the following way:
Code: [Select]
logic [15:0] sig;
logic [21:0] out_i;
logic [21:0] out_q;
logic [63:0] phase_word;    // see description below

cordic 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: [Select]
logic [31:0] freq_hz;

// M2 = 2^57 / OSCILLATOR_CLOCK_HZ
localparam M2 = (64'd1 << 57) / OSCILLATOR_CLOCK_HZ;

// M3 = 2^24
localparam 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.v

in 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: [Select]
localparam M2 = (64'd1 << 57) / 64'd1966080000;

localparam M3 = 32'd16777216;

assign phase_word = 32'd1000000 * M2 + M3;
« Last Edit: July 05, 2022, 05:14:44 am by radiolistener »
 
The following users thanked this post: blackblade

Offline radiolistener

  • Super Contributor
  • ***
  • Posts: 2274
  • Country: ua
Re: Python Script to Verilog RTL
« Reply #8 on: July 05, 2022, 05:21:46 am »
I have the RTL block which can generate sine and cosine wave.

if you already have NCO module which produces sine and cosine wave, then just use multiply operation:
Code: [Select]
logic        clock; // ADC clock
logic [15:0] sig; // ADC sample
logic [15:0] nco_sin;
logic [15:0] nco_cos;
logic [31:0] out_i;
logic [31:0] out_q;

always @(posedge clock) begin
    out_i <= nco_cos * sig;
    out_q <= nco_sin * sig;
end

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.

there 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.
« Last Edit: July 05, 2022, 05:48:18 am by radiolistener »
 
The following users thanked this post: blackblade


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf