hz12opensource / libresdr

Firmware with overclock support for LibreSDR (PlutoSDR clone with Zynq 7020), 27.5 MSPS sample rate over Gigabit Ethernet with libiio/PlutoSDR API
GNU General Public License v3.0
69 stars 9 forks source link

How to use PPS #10

Open wonshil opened 4 months ago

wonshil commented 4 months ago

How to use PPS?

Could I know some pps using example?

Pytlicek commented 1 month ago

Not related to PPS, But I ordered 10MHz OXCO from Aliexpress: https://vi.aliexpress.com/item/1005007211198066.html And MMCX male to SMA Male cable: https://vi.aliexpress.com/item/1005006716008066.html On LibreSDR there is MMCX port on board.

Screenshot 2024-10-04 at 09 28 44
Denisfk1985 commented 1 month ago

In the standard firmware of the manufacturer PPS and 10MHz interface it works. And also on VCTCXO half of the voltage and it gives a fairly accurate frequency. But in the current project and all that I came across, these functions do not work, they need to be implemented in HDL, for example, to adjust the frequency you need to supply voltage to VCTCXO, it comes from the DAC U9, which in turn is controlled by the SPI bus.

Pytlicek commented 1 month ago

@Denisfk1985 "standard firmware" means from day0wl: https://github.com/day0wl/libresdr-fw ?

Denisfk1985 commented 1 month ago

I meant the firmware that comes with LibreSDR when purchased. I bought it a long time ago, and back then, LibreSDR was supplied with a pre-installed and pre-written microSD card. Now, the device is shipped with an empty memory card, but the seller provides a link to the firmware. I'm not sure what firmware is currently supplied; it’s probably the same as before, where the external synchronization ports and frequency correction for the crystal oscillator work correctly.

Shawn-McSorley commented 3 days ago

Hi all,

I've made some progress here. The best place to look is at the microphase ant sdr source code. The libre sdr is likely a clone of the e310.

The main differences are the DAC chip and ports used by the FPGA. I've successfully modulated the frequency of the onboard VCTCXO.

The following constraints need to be added to the xdc file

set_property -dict {PACKAGE_PIN H18 IOSTANDARD LVCMOS33} [get_ports CLK_40M_DAC_nSYNC];
set_property -dict {PACKAGE_PIN F19 IOSTANDARD LVCMOS33} [get_ports CLK_40M_DAC_SCLK];
set_property -dict {PACKAGE_PIN F20 IOSTANDARD LVCMOS33} [get_ports CLK_40M_DAC_DIN];
set_property -dict {PACKAGE_PIN K18 IOSTANDARD LVCMOS33} [get_ports PPS_IN];
set_property -dict {PACKAGE_PIN H16 IOSTANDARD LVCMOS33} [get_ports CLK_40MHz_FPGA];
set_property -dict {PACKAGE_PIN K17 IOSTANDARD LVCMOS33} [get_ports CLKIN_10MHz]; # Same bank as CLK_40MHz_FPGA. 3.3V on data sheet.

The ports should also be created in the system_bd.tcl file

create_bd_port -dir I CLKIN_10MHz
create_bd_port -dir I CLK_40MHz_FPGA
create_bd_port -dir O CLK_40M_DAC_DIN
create_bd_port -dir O CLK_40M_DAC_SCLK
create_bd_port -dir O CLK_40M_DAC_nSYNC
create_bd_port -dir I PPS_IN

The ant sdr source code uses verilog modules made by Ettus research. The SPI driver code can be modified to support the chip on the libre sdr:

//
// Copyright 2015 Ettus Research, a National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//

`timescale 1ns / 1ps

// This code is intended for a dac5311 module (8-bit DAC). Though the data register should accompany the 12-bit version.
// Some notes from data sheet:
// 1. Max clock frequency of sclk is 20MHz when Vdd is 3.3V (see libre SDR schematic)
// 2. sclk always runs
// 3. See pg. 7 of data sheet for timing diagram. ~SYNC goes high. Data is clocked in on the next falling edge of sclk, after ~SYNC goes low.
// 4. 16 data bits are clocked in (16 sclk cycles afer ~SYNC goes low).
// 5. First two bits are operating mode. 00 is normal operation, 01 is 1kOhm to ground, 10 is 100kOhm to ground, 11 is high-z.
// 6. MSBs are clocked in first. Starting with operating mode, then 12-bit data. Rest is don't care.
// 7. ~SYNC must be low for at least 16 sclk cycles during write, otherwise data is ignored. 
// 8. ~SYNC must be high for at least 20ns before the next write. Falling edge of ~SYNC triggers write. 

// This code is a minor modification of ltc2630_spi.v by Ettus Research. 

module dacxx11_spi  (
    input   wire            clk,
    input   wire            rst,
    input   wire  [11:0]    data, // 12-bit data, 8 bits in MSBs
    output  reg             sclk,
    output  wire            mosi,
    output  reg             sync_n
);

//====================================================
//parameter define
//====================================================
localparam  IDLE        = 4'b0001;
localparam  SYNC_PRE    = 4'b0010;
localparam  DATA        = 4'b0100;
localparam  SYNC_END    = 4'b1000;

localparam NORMAL_OPERATION = 2'b00;
localparam K1KOHM_TO_GROUND = 2'b01;
localparam K100KOHM_TO_GROUND = 2'b10;
localparam HIGH_Z = 2'b11;

//====================================================
// internal signals and registers
//====================================================
reg     [3:0]   state;
reg     [4:0]   cnt_cycle   ;
reg     [5:0]   cnt_bit     ;
reg     [11:0]  last_data   ;
reg     [15:0]  data_shift  ;
wire            rising_edge ;
wire            falling_edge;

//----------------state------------------
always @(posedge clk ) begin
    if (rst==1'b1) begin
        state <= IDLE;
    end
    else  begin
        case (state)
            IDLE : begin
                // detect a new data input, the dac value needs to be updated
                if (last_data != data) begin
                    state <= SYNC_PRE;
                end
            end

            SYNC_PRE : begin
                // The SYNC is low, start to update the value
                if (falling_edge) begin
                    state <= DATA;
                end
            end

            DATA : begin
                if (cnt_bit == 'd15 && falling_edge) begin
                    state <= SYNC_END;
                end
            end

            SYNC_END : begin
                if (rising_edge == 1'b1) begin
                    state <= IDLE;
                end
            end
        endcase
    end
end

//----------------cnt_cycle------------------
always @(posedge clk ) begin
    if (rst==1'b1) begin
        cnt_cycle <= 'd0;
    end
    else if (state == SYNC_PRE || state == DATA || state == SYNC_END) begin
        cnt_cycle <= cnt_cycle + 1'b1;
    end
    else  begin
        cnt_cycle <=  'd0;
    end
end

assign rising_edge = cnt_cycle==5'b10000; // Modified so that 200M clock is divided by 16 not 8.
assign falling_edge = cnt_cycle==5'b11111;

//----------------data_shift------------------
always @(posedge clk ) begin
    if (rst==1'b1) begin
        data_shift <= 'd0;
    end
    else if (state == IDLE && (last_data != data)) begin
        data_shift <= {NORMAL_OPERATION, data};
    end
    else if (state == DATA && falling_edge) begin
        data_shift <=  {data_shift[14:0], 1'b0};
    end
end

//----------------cnt_bit------------------
always @(posedge clk ) begin
    if (rst==1'b1) begin
        cnt_bit <= 'd0;
    end
    else if (state == DATA ) begin
        if (cnt_bit == 'd15 && falling_edge) begin
            cnt_bit <= 'd0;
        end
        else if(falling_edge)begin
            cnt_bit <= cnt_bit + 1'b1;
        end
    end
    else  begin
        cnt_bit <=  'd0;
    end
end

//----------------last_data------------------
always @(posedge clk ) begin
    if (rst==1'b1) begin
        last_data <= 'd0;
    end
    else if (state == IDLE && (last_data != data)) begin
        last_data <= data;
    end
end

//-----------------sclk-----------------
always @(posedge clk ) begin
    if (rst==1'b1) begin
        sclk <= 1'b0;
    end
    else if (rising_edge == 1'b1) begin // removed state==DATA &&
        sclk <= 1'b1;
    end
    else if (falling_edge == 1'b1) begin // removed state==DATA &&
        sclk <=  1'b0;
    end
end

assign mosi = data_shift[15];

//----------------sync_n------------------
always @(posedge clk ) begin
    if (rst==1'b1) begin
        sync_n <= 1'b1;
    end
    else if (state == SYNC_PRE && falling_edge == 1'b1) begin
        sync_n <= 1'b0;
    end
    else if (state == SYNC_END && rising_edge == 1'b1) begin
        sync_n <=  1'b1;
    end
end

endmodule

In the ant sdr source code, there's a module called axi_vcxo_ctrl which handles the 1PPS and 10MHz discipline logic. With the above modifications, this should be fairly easy to port to the libre sdr. I don't have a connector for the 1PPS or 10MHz port so can't check at the moment. I'll likely try discipline the VCTCXO off one of the RF inputs first.

I haven't exhaustively checked that the above code works. To test it I set one of the RF outputs to a constant frequency. I then used a phasemeter to measure its frequency, while changing the DAC input with a GPIO module. I was able to see the frequency of the RF output step up and down accordingly. If I make more progress here I may package it into new firmware.