Using remotesyn and added NCO
This commit is contained in:
9
.gitignore
vendored
9
.gitignore
vendored
@@ -1,6 +1,3 @@
|
|||||||
impl
|
out
|
||||||
src
|
build
|
||||||
|
env
|
||||||
*.vcd
|
|
||||||
*.log
|
|
||||||
cvcsim*
|
|
||||||
20
CON/io.cst
20
CON/io.cst
@@ -1,20 +0,0 @@
|
|||||||
//Copyright (C)2014-2025 Gowin Semiconductor Corporation.
|
|
||||||
//All rights reserved.
|
|
||||||
//File Title: Physical Constraints file
|
|
||||||
//Tool Version: V1.9.12
|
|
||||||
//Part Number: GW1NSR-LV4CQN48PC7/I6
|
|
||||||
//Device: GW1NSR-4C
|
|
||||||
//Created Time: Wed 10 01 18:02:30 2025
|
|
||||||
|
|
||||||
IO_LOC "adc1_A" 39,40;
|
|
||||||
IO_PORT "adc1_A" IO_TYPE=LVDS25 PULL_MODE=NONE BANK_VCCIO=3.3;
|
|
||||||
IO_LOC "adc1_O" 41;
|
|
||||||
IO_PORT "adc1_O" IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
|
||||||
IO_LOC "led" 10;
|
|
||||||
IO_PORT "led" IO_TYPE=LVCMOS33 PULL_MODE=NONE DRIVE=8 BANK_VCCIO=3.3;
|
|
||||||
IO_LOC "reset_n" 15;
|
|
||||||
IO_PORT "reset_n" IO_TYPE=LVCMOS18 PULL_MODE=UP BANK_VCCIO=1.8;
|
|
||||||
IO_LOC "clk" 45;
|
|
||||||
IO_PORT "clk" IO_TYPE=LVCMOS33 PULL_MODE=UP BANK_VCCIO=3.3;
|
|
||||||
IO_LOC "button" 14;
|
|
||||||
IO_PORT "button" IO_TYPE=LVCMOS18 PULL_MODE=UP BANK_VCCIO=1.8;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
//Copyright (C)2014-2025 GOWIN Semiconductor Corporation.
|
|
||||||
//All rights reserved.
|
|
||||||
//File Title: Timing Constraints file
|
|
||||||
//Tool Version: V1.9.12
|
|
||||||
//Created Time: 2025-10-01 16:50:37
|
|
||||||
create_clock -name CLK_IN -period 37.037 -waveform {0 18.518} [get_ports {clk}]
|
|
||||||
create_clock -name CLK_120 -period 8.333 -waveform {0 4.167} [get_nets {clk_120}]
|
|
||||||
create_clock -name CLK_15 -period 66.666 -waveform {0 33.333} [get_nets {clk_15}]
|
|
||||||
133
HW/sampling.v
133
HW/sampling.v
@@ -1,133 +0,0 @@
|
|||||||
`timescale 1ns/1ps
|
|
||||||
|
|
||||||
module sampling(
|
|
||||||
input wire adc_A,
|
|
||||||
input wire adc_B,
|
|
||||||
output wire adc_O,
|
|
||||||
|
|
||||||
input wire clk_15,
|
|
||||||
input wire clk_120,
|
|
||||||
input wire reset_n
|
|
||||||
);
|
|
||||||
wire sigmadelta_sample;
|
|
||||||
|
|
||||||
sigmadelta_sampler m_sdsampler(
|
|
||||||
.clk(clk_15),
|
|
||||||
.A(adc_A),
|
|
||||||
.B(adc_B),
|
|
||||||
.out(sigmadelta_sample)
|
|
||||||
);
|
|
||||||
assign adc_O = sigmadelta_sample;
|
|
||||||
|
|
||||||
// RC model, output is y_next_q15
|
|
||||||
// ------------------------------
|
|
||||||
reg signed [15:0] y_q15;
|
|
||||||
wire signed [15:0] x_q15 = sigmadelta_sample ? 16'sh7fff : 16'sh0000;
|
|
||||||
wire signed [15:0] e_q15 = x_q15 - y_q15;
|
|
||||||
wire signed [31:0] prod_q30 = $signed(16'sh0b00) * $signed(e_q15); // factor should be 0b3b, used bit simplified here
|
|
||||||
wire signed [15:0] delta_q15 = prod_q30 >>> 15;
|
|
||||||
wire signed [15:0] y_next_q15 = y_q15 + delta_q15;
|
|
||||||
|
|
||||||
// Optional clamp to [0, 0x7FFF] (keeps GTKWave tidy)
|
|
||||||
function signed [15:0] clamp01_q15(input signed [15:0] v);
|
|
||||||
if (v < 16'sd0000) clamp01_q15 = 16'sd0000;
|
|
||||||
else if (v > 16'sh7FFF) clamp01_q15 = 16'sh7FFF;
|
|
||||||
else clamp01_q15 = v;
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
always @(posedge clk_15 or negedge reset_n) begin
|
|
||||||
if (!reset_n) y_q15 <= 16'sd0000;
|
|
||||||
else y_q15 <= clamp01_q15(y_next_q15);
|
|
||||||
end
|
|
||||||
// ------------------------------
|
|
||||||
|
|
||||||
wire signed [15:0] lpfed_q15;
|
|
||||||
lpf_iir_q15 #(.K(7)) m_lpf (
|
|
||||||
.clk(clk_15),
|
|
||||||
.rst_n(reset_n),
|
|
||||||
.x_q15(y_next_q15),
|
|
||||||
.y_q15(lpfed_q15)
|
|
||||||
);
|
|
||||||
|
|
||||||
wire signed [15:0] decimated_q15;
|
|
||||||
decimate_by_r #(.R(375)) m_decimator (
|
|
||||||
.clk(clk_15),
|
|
||||||
.rst_n(reset_n),
|
|
||||||
.in_valid(1'b1),
|
|
||||||
.in_q15(lpfed_q15),
|
|
||||||
.out_valid(),
|
|
||||||
.out_q15(decimated_q15)
|
|
||||||
);
|
|
||||||
|
|
||||||
// decimated_q15 + out_valid @ 40KHz
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
// Simple 1-pole IIR LPF, Q1.15 in/out, multiplier-less (alpha = 1/2^K)
|
|
||||||
// --------------------------------------------------------------------
|
|
||||||
module lpf_iir_q15 #(
|
|
||||||
parameter integer K = 10 // try 8..12; bigger = more smoothing
|
|
||||||
)(
|
|
||||||
input wire clk,
|
|
||||||
input wire rst_n,
|
|
||||||
input wire signed [15:0] x_q15, // Q1.15 input (e.g., 0..0x7FFF)
|
|
||||||
output reg signed [15:0] y_q15 // Q1.15 output
|
|
||||||
);
|
|
||||||
wire signed [15:0] e_q15 = x_q15 - y_q15;
|
|
||||||
wire signed [15:0] delta_q15 = e_q15 >>> K; // arithmetic shift
|
|
||||||
wire signed [15:0] y_next = y_q15 + delta_q15;
|
|
||||||
|
|
||||||
// clamp to [0, 0x7FFF] (handy if your signal is non-negative)
|
|
||||||
function signed [15:0] clamp01;
|
|
||||||
input signed [15:0] v;
|
|
||||||
begin
|
|
||||||
if (v < 16'sd0) clamp01 = 16'sd0;
|
|
||||||
else if (v > 16'sh7FFF) clamp01 = 16'sh7FFF;
|
|
||||||
else clamp01 = v;
|
|
||||||
end
|
|
||||||
endfunction
|
|
||||||
|
|
||||||
always @(posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) y_q15 <= 16'sd0;
|
|
||||||
else y_q15 <= clamp01(y_next);
|
|
||||||
end
|
|
||||||
endmodule
|
|
||||||
|
|
||||||
// -------------------------------------------------------------
|
|
||||||
// Decimate-by-R: passes through one sample every R input clocks
|
|
||||||
// No $clog2 used; set CNT_W wide enough for your R.
|
|
||||||
// -------------------------------------------------------------
|
|
||||||
module decimate_by_r #(
|
|
||||||
parameter integer R = 400, // decimation factor
|
|
||||||
parameter integer CNT_W = 10 // width so that 2^CNT_W > R (e.g., 10 for 750)
|
|
||||||
)(
|
|
||||||
input wire clk,
|
|
||||||
input wire rst_n,
|
|
||||||
input wire in_valid, // assert 1'b1 if always valid
|
|
||||||
input wire signed [15:0] in_q15, // Q1.15 sample at full rate
|
|
||||||
output reg out_valid, // 1-cycle pulse every R samples
|
|
||||||
output reg signed [15:0] out_q15 // Q1.15 sample at decimated rate
|
|
||||||
);
|
|
||||||
reg [CNT_W-1:0] cnt;
|
|
||||||
|
|
||||||
always @(posedge clk or negedge rst_n) begin
|
|
||||||
if (!rst_n) begin
|
|
||||||
cnt <= {CNT_W{1'b0}};
|
|
||||||
out_valid <= 1'b0;
|
|
||||||
out_q15 <= 16'sd0;
|
|
||||||
end else begin
|
|
||||||
out_valid <= 1'b0;
|
|
||||||
|
|
||||||
if (in_valid) begin
|
|
||||||
if (cnt == R-1) begin
|
|
||||||
cnt <= {CNT_W{1'b0}};
|
|
||||||
out_q15 <= in_q15;
|
|
||||||
out_valid <= 1'b1;
|
|
||||||
end else begin
|
|
||||||
cnt <= cnt + 1'b1;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
endmodule
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
`timescale 1ns/1ps
|
|
||||||
|
|
||||||
module sigmadelta_sampler(
|
|
||||||
input wire clk,
|
|
||||||
input wire A,
|
|
||||||
input wire B,
|
|
||||||
output wire out
|
|
||||||
);
|
|
||||||
|
|
||||||
wire O;
|
|
||||||
reg out_r;
|
|
||||||
|
|
||||||
TLVDS_IBUF m_cmp(
|
|
||||||
.I(A),
|
|
||||||
.IB(B),
|
|
||||||
.O(O)
|
|
||||||
);
|
|
||||||
|
|
||||||
always @(posedge clk) begin
|
|
||||||
out_r = O;
|
|
||||||
end
|
|
||||||
assign out = out_r;
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
`timescale 1ns/1ps
|
|
||||||
|
|
||||||
module toplevel(
|
|
||||||
input wire clk,
|
|
||||||
input wire reset_n,
|
|
||||||
|
|
||||||
input wire button,
|
|
||||||
output wire led,
|
|
||||||
|
|
||||||
input wire adc1_A,
|
|
||||||
input wire adc1_B,
|
|
||||||
output wire adc1_O
|
|
||||||
);
|
|
||||||
reg led_v;
|
|
||||||
wire led_i;
|
|
||||||
wire clk_120;
|
|
||||||
wire clk_15;
|
|
||||||
|
|
||||||
gw_pllvr m_pll(
|
|
||||||
.clkout(clk_120),
|
|
||||||
.reset(!reset_n),
|
|
||||||
.clkin(clk)
|
|
||||||
);
|
|
||||||
gw_clkdiv8 m_clkdiv8(
|
|
||||||
.clkout(clk_15),
|
|
||||||
.hclkin(clk_120),
|
|
||||||
.resetn(reset_n)
|
|
||||||
);
|
|
||||||
|
|
||||||
always @(posedge clk_120 or negedge reset_n) begin
|
|
||||||
if (!reset_n) begin
|
|
||||||
led_v <= 1'b0;
|
|
||||||
end else begin
|
|
||||||
led_v <= led_i;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
assign led = led_v;
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
[General]
|
|
||||||
file=gw_clkdiv8
|
|
||||||
ipc_version=4
|
|
||||||
module=gw_clkdiv8
|
|
||||||
target_device=gw1nsr4c-009
|
|
||||||
type=clock_clkdiv
|
|
||||||
version=1.0
|
|
||||||
|
|
||||||
[Config]
|
|
||||||
Calibration=false
|
|
||||||
Division_Factor=8
|
|
||||||
Language=0
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
-series GW1NSR
|
|
||||||
-device GW1NSR-4C
|
|
||||||
-device_version
|
|
||||||
-package QFN48P
|
|
||||||
-part_number GW1NSR-LV4CQN48PC7/I6
|
|
||||||
|
|
||||||
|
|
||||||
-mod_name gw_clkdiv8
|
|
||||||
-file_name gw_clkdiv8
|
|
||||||
-path /data/joppe/projects/modem/IP/gowin_clkdiv/
|
|
||||||
-type CLKDIV
|
|
||||||
-file_type vlg
|
|
||||||
-division_factor 8
|
|
||||||
-calib false
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//Copyright (C)2014-2025 Gowin Semiconductor Corporation.
|
|
||||||
//All rights reserved.
|
|
||||||
//File Title: IP file
|
|
||||||
//Tool Version: V1.9.12
|
|
||||||
//Part Number: GW1NSR-LV4CQN48PC7/I6
|
|
||||||
//Device: GW1NSR-4C
|
|
||||||
//Created Time: Wed Oct 1 18:23:11 2025
|
|
||||||
|
|
||||||
module gw_clkdiv8 (clkout, hclkin, resetn);
|
|
||||||
|
|
||||||
output clkout;
|
|
||||||
input hclkin;
|
|
||||||
input resetn;
|
|
||||||
|
|
||||||
wire gw_gnd;
|
|
||||||
|
|
||||||
assign gw_gnd = 1'b0;
|
|
||||||
|
|
||||||
CLKDIV clkdiv_inst (
|
|
||||||
.CLKOUT(clkout),
|
|
||||||
.HCLKIN(hclkin),
|
|
||||||
.RESETN(resetn),
|
|
||||||
.CALIB(gw_gnd)
|
|
||||||
);
|
|
||||||
|
|
||||||
defparam clkdiv_inst.DIV_MODE = "8";
|
|
||||||
defparam clkdiv_inst.GSREN = "false";
|
|
||||||
|
|
||||||
endmodule //gw_clkdiv8
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//Copyright (C)2014-2025 Gowin Semiconductor Corporation.
|
|
||||||
//All rights reserved.
|
|
||||||
//File Title: Template file for instantiation
|
|
||||||
//Tool Version: V1.9.12
|
|
||||||
//Part Number: GW1NSR-LV4CQN48PC7/I6
|
|
||||||
//Device: GW1NSR-4C
|
|
||||||
//Created Time: Wed Oct 1 18:23:11 2025
|
|
||||||
|
|
||||||
//Change the instance name and port connections to the signal names
|
|
||||||
//--------Copy here to design--------
|
|
||||||
|
|
||||||
gw_clkdiv8 your_instance_name(
|
|
||||||
.clkout(clkout), //output clkout
|
|
||||||
.hclkin(hclkin), //input hclkin
|
|
||||||
.resetn(resetn) //input resetn
|
|
||||||
);
|
|
||||||
|
|
||||||
//--------Copy end-------------------
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
[General]
|
|
||||||
file=gw_pllvr
|
|
||||||
ipc_version=4
|
|
||||||
module=gw_pllvr
|
|
||||||
target_device=gw1nsr4c-009
|
|
||||||
type=clock_pllvr
|
|
||||||
version=1.0
|
|
||||||
|
|
||||||
[Config]
|
|
||||||
CKLOUTD3=false
|
|
||||||
CLKFB_SOURCE=0
|
|
||||||
CLKIN_FREQ=27
|
|
||||||
CLKOUTD=false
|
|
||||||
CLKOUTP=false
|
|
||||||
CLKOUT_BYPASS=false
|
|
||||||
CLKOUT_DIVIDE_DYN=true
|
|
||||||
CLKOUT_FREQ=120
|
|
||||||
CLKOUT_TOLERANCE=0
|
|
||||||
DYNAMIC=true
|
|
||||||
LANG=0
|
|
||||||
LOCK_EN=false
|
|
||||||
MODE_GENERAL=true
|
|
||||||
PLL_PWD=false
|
|
||||||
PLL_REGULATOR=false
|
|
||||||
RESET_PLL=true
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
-series GW1NSR
|
|
||||||
-device GW1NSR-4C
|
|
||||||
-device_version
|
|
||||||
-package QFN48P
|
|
||||||
-part_number GW1NSR-LV4CQN48PC7/I6
|
|
||||||
|
|
||||||
|
|
||||||
-mod_name gw_pllvr
|
|
||||||
-file_name gw_pllvr
|
|
||||||
-path /data/joppe/projects/modem/IP/ge_pllvr/
|
|
||||||
-type PLL
|
|
||||||
-pllvr true
|
|
||||||
-file_type vlg
|
|
||||||
-dev_type GW1NSR-4C
|
|
||||||
-dyn_idiv_sel false
|
|
||||||
-idiv_sel 9
|
|
||||||
-dyn_fbdiv_sel false
|
|
||||||
-fbdiv_sel 40
|
|
||||||
-dyn_odiv_sel false
|
|
||||||
-odiv_sel 8
|
|
||||||
-dyn_da_en true
|
|
||||||
-rst_sig true
|
|
||||||
-rst_sig_p false
|
|
||||||
-pll_reg false
|
|
||||||
-fclkin 27
|
|
||||||
-clkfb_sel 0
|
|
||||||
-en_lock false
|
|
||||||
-clkout_bypass false
|
|
||||||
-clkout_ft_dir 1
|
|
||||||
-en_clkoutp false
|
|
||||||
-clkoutp_bypass false
|
|
||||||
-en_clkoutd false
|
|
||||||
-clkoutd_bypass false
|
|
||||||
-en_clkoutd3 false
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
//Copyright (C)2014-2025 Gowin Semiconductor Corporation.
|
|
||||||
//All rights reserved.
|
|
||||||
//File Title: IP file
|
|
||||||
//Tool Version: V1.9.12
|
|
||||||
//Part Number: GW1NSR-LV4CQN48PC7/I6
|
|
||||||
//Device: GW1NSR-4C
|
|
||||||
//Created Time: Wed Oct 1 13:08:32 2025
|
|
||||||
|
|
||||||
module gw_pllvr (clkout, reset, clkin);
|
|
||||||
|
|
||||||
output clkout;
|
|
||||||
input reset;
|
|
||||||
input clkin;
|
|
||||||
|
|
||||||
wire lock_o;
|
|
||||||
wire clkoutp_o;
|
|
||||||
wire clkoutd_o;
|
|
||||||
wire clkoutd3_o;
|
|
||||||
wire gw_vcc;
|
|
||||||
wire gw_gnd;
|
|
||||||
|
|
||||||
assign gw_vcc = 1'b1;
|
|
||||||
assign gw_gnd = 1'b0;
|
|
||||||
|
|
||||||
PLLVR pllvr_inst (
|
|
||||||
.CLKOUT(clkout),
|
|
||||||
.LOCK(lock_o),
|
|
||||||
.CLKOUTP(clkoutp_o),
|
|
||||||
.CLKOUTD(clkoutd_o),
|
|
||||||
.CLKOUTD3(clkoutd3_o),
|
|
||||||
.RESET(reset),
|
|
||||||
.RESET_P(gw_gnd),
|
|
||||||
.CLKIN(clkin),
|
|
||||||
.CLKFB(gw_gnd),
|
|
||||||
.FBDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
|
||||||
.IDSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
|
||||||
.ODSEL({gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
|
||||||
.PSDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
|
||||||
.DUTYDA({gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
|
||||||
.FDLY({gw_gnd,gw_gnd,gw_gnd,gw_gnd}),
|
|
||||||
.VREN(gw_vcc)
|
|
||||||
);
|
|
||||||
|
|
||||||
defparam pllvr_inst.FCLKIN = "27";
|
|
||||||
defparam pllvr_inst.DYN_IDIV_SEL = "false";
|
|
||||||
defparam pllvr_inst.IDIV_SEL = 8;
|
|
||||||
defparam pllvr_inst.DYN_FBDIV_SEL = "false";
|
|
||||||
defparam pllvr_inst.FBDIV_SEL = 39;
|
|
||||||
defparam pllvr_inst.DYN_ODIV_SEL = "false";
|
|
||||||
defparam pllvr_inst.ODIV_SEL = 8;
|
|
||||||
defparam pllvr_inst.PSDA_SEL = "0000";
|
|
||||||
defparam pllvr_inst.DYN_DA_EN = "true";
|
|
||||||
defparam pllvr_inst.DUTYDA_SEL = "1000";
|
|
||||||
defparam pllvr_inst.CLKOUT_FT_DIR = 1'b1;
|
|
||||||
defparam pllvr_inst.CLKOUTP_FT_DIR = 1'b1;
|
|
||||||
defparam pllvr_inst.CLKOUT_DLY_STEP = 0;
|
|
||||||
defparam pllvr_inst.CLKOUTP_DLY_STEP = 0;
|
|
||||||
defparam pllvr_inst.CLKFB_SEL = "internal";
|
|
||||||
defparam pllvr_inst.CLKOUT_BYPASS = "false";
|
|
||||||
defparam pllvr_inst.CLKOUTP_BYPASS = "false";
|
|
||||||
defparam pllvr_inst.CLKOUTD_BYPASS = "false";
|
|
||||||
defparam pllvr_inst.DYN_SDIV_SEL = 2;
|
|
||||||
defparam pllvr_inst.CLKOUTD_SRC = "CLKOUT";
|
|
||||||
defparam pllvr_inst.CLKOUTD3_SRC = "CLKOUT";
|
|
||||||
defparam pllvr_inst.DEVICE = "GW1NSR-4C";
|
|
||||||
|
|
||||||
endmodule //gw_pllvr
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
//Copyright (C)2014-2025 Gowin Semiconductor Corporation.
|
|
||||||
//All rights reserved.
|
|
||||||
//File Title: Template file for instantiation
|
|
||||||
//Tool Version: V1.9.12
|
|
||||||
//Part Number: GW1NSR-LV4CQN48PC7/I6
|
|
||||||
//Device: GW1NSR-4C
|
|
||||||
//Created Time: Wed Oct 1 13:08:32 2025
|
|
||||||
|
|
||||||
//Change the instance name and port connections to the signal names
|
|
||||||
//--------Copy here to design--------
|
|
||||||
|
|
||||||
gw_pllvr your_instance_name(
|
|
||||||
.clkout(clkout), //output clkout
|
|
||||||
.reset(reset), //input reset
|
|
||||||
.clkin(clkin) //input clkin
|
|
||||||
);
|
|
||||||
|
|
||||||
//--------Copy end-------------------
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
`timescale 1ns/1ps
|
|
||||||
module glbl;
|
|
||||||
reg gsri = 0;
|
|
||||||
initial begin
|
|
||||||
#10 gsri = 1; // release reset after 100ns
|
|
||||||
end
|
|
||||||
|
|
||||||
GSR GSR (.GSRI(gsri));
|
|
||||||
endmodule
|
|
||||||
18141
SIM/prim_sim.v
18141
SIM/prim_sim.v
File diff suppressed because it is too large
Load Diff
129111
SIM/prim_tsim.v
129111
SIM/prim_tsim.v
File diff suppressed because it is too large
Load Diff
@@ -1,30 +0,0 @@
|
|||||||
`timescale 1ns/1ps
|
|
||||||
|
|
||||||
module sampling_tb;
|
|
||||||
reg clk_15;
|
|
||||||
reg clk_120;
|
|
||||||
reg reset_n;
|
|
||||||
|
|
||||||
sampling m_sampling(
|
|
||||||
.clk_15(clk_15),
|
|
||||||
.clk_120(clk_120),
|
|
||||||
.reset_n(reset_n)
|
|
||||||
);
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
$dumpfile("sampling_tb.vcd");
|
|
||||||
$dumpvars (0, sampling_tb);
|
|
||||||
|
|
||||||
clk_15 <= 1'b0;
|
|
||||||
clk_120 <= 1'b0;
|
|
||||||
reset_n <= 1'b0;
|
|
||||||
#50 reset_n <= 1'b1;
|
|
||||||
|
|
||||||
#2000000
|
|
||||||
$finish;
|
|
||||||
end
|
|
||||||
|
|
||||||
always #(500/15) clk_15 = ~clk_15;
|
|
||||||
always #(500/120) clk_120 = ~clk_120;
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
`timescale 1ns/1ps
|
|
||||||
|
|
||||||
module sigmadelta_sampler(
|
|
||||||
input wire clk,
|
|
||||||
input wire A,
|
|
||||||
input wire B,
|
|
||||||
output wire out
|
|
||||||
);
|
|
||||||
// ===== Tunable parameters =====
|
|
||||||
// Sine source (A input / P)
|
|
||||||
parameter real F_HZ = 1.5e3; // input sine frequency (1 kHz)
|
|
||||||
parameter real AMP = 1.5; // sine amplitude (V)
|
|
||||||
parameter real VCM = 1.65; // common-mode (V), centered in 0..3.3V
|
|
||||||
|
|
||||||
// Comparator behavior
|
|
||||||
parameter real VTH = 0.0; // threshold on (vp - vn)
|
|
||||||
parameter real VHYST = 0.05; // symmetric hysteresis half-width (V)
|
|
||||||
parameter integer ADD_HYST = 0; // 1 to enable hysteresis
|
|
||||||
|
|
||||||
// 1-bit DAC rails (feedback into RC)
|
|
||||||
parameter real VLOW = 0.0; // DAC 0 (V)
|
|
||||||
parameter real VHIGH = 3.3; // DAC 1 (V)
|
|
||||||
|
|
||||||
// RC filter (B input / N)
|
|
||||||
parameter real R_OHMS = 3300.0; // 3.3k
|
|
||||||
parameter real C_FARADS = 220e-12; // 220 pF
|
|
||||||
|
|
||||||
// Integration step (ties to `timescale`)
|
|
||||||
parameter integer TSTEP_NS = 10; // sim step in ns (choose << tau)
|
|
||||||
|
|
||||||
// ===== Internal state (simulation only) =====
|
|
||||||
real vp, vn; // comparator A/B inputs
|
|
||||||
real v_rc; // RC node voltage (== vn)
|
|
||||||
real v_dac; // DAC output voltage from O
|
|
||||||
real t_s; // time in seconds
|
|
||||||
real dt_s; // step in seconds
|
|
||||||
real tau_s; // R*C time constant in seconds
|
|
||||||
real two_pi;
|
|
||||||
reg q; // comparator latched output (pre-delay)
|
|
||||||
reg O;
|
|
||||||
reg sampler;
|
|
||||||
|
|
||||||
initial sampler <= 1'b0;
|
|
||||||
always @(posedge clk) begin
|
|
||||||
sampler <= O;
|
|
||||||
end
|
|
||||||
assign out = sampler;
|
|
||||||
|
|
||||||
|
|
||||||
// Helper task: update comparator with optional hysteresis
|
|
||||||
task automatic comp_update;
|
|
||||||
real diff;
|
|
||||||
begin
|
|
||||||
diff = (vp - vn);
|
|
||||||
|
|
||||||
if (ADD_HYST != 0) begin
|
|
||||||
// simple symmetric hysteresis around VTH
|
|
||||||
if (q && (diff < (VTH - VHYST))) q = 1'b0;
|
|
||||||
else if (!q && (diff > (VTH + VHYST))) q = 1'b1;
|
|
||||||
// else hold
|
|
||||||
end else begin
|
|
||||||
q = (diff > VTH) ? 1'b1 : 1'b0;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
endtask
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
// Init constants
|
|
||||||
two_pi = 6.283185307179586;
|
|
||||||
tau_s = R_OHMS * C_FARADS; // ~7.26e-7 s
|
|
||||||
dt_s = TSTEP_NS * 1.0e-9;
|
|
||||||
|
|
||||||
// Init states
|
|
||||||
t_s = 0.0;
|
|
||||||
q = 1'b0; // start low
|
|
||||||
O = 1'b0;
|
|
||||||
v_dac= VLOW;
|
|
||||||
v_rc = (VHIGH + VLOW)/2.0; // start mid-rail to reduce start-up transient
|
|
||||||
vn = v_rc;
|
|
||||||
vp = VCM;
|
|
||||||
|
|
||||||
// Main sim loop
|
|
||||||
forever begin
|
|
||||||
#(TSTEP_NS); // advance discrete time step
|
|
||||||
t_s = t_s + dt_s;
|
|
||||||
|
|
||||||
// 1) Update DAC from previous comparator state
|
|
||||||
v_dac = sampler ? VHIGH : VLOW;
|
|
||||||
|
|
||||||
// 2) RC low-pass driven by DAC: Euler step
|
|
||||||
// dv = (v_dac - v_rc) * dt/tau
|
|
||||||
v_rc = v_rc + (v_dac - v_rc) * (dt_s / tau_s);
|
|
||||||
vn = v_rc;
|
|
||||||
|
|
||||||
// 3) Input sine on A
|
|
||||||
vp = VCM + AMP * $sin(two_pi * F_HZ * t_s);
|
|
||||||
|
|
||||||
// 4) Comparator decision (with optional hysteresis)
|
|
||||||
comp_update();
|
|
||||||
|
|
||||||
// 5) Output with propagation delay
|
|
||||||
O = q;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
`timescale 1ns/1ps
|
|
||||||
|
|
||||||
module toplevel_tb;
|
|
||||||
reg clk;
|
|
||||||
reg reset_n;
|
|
||||||
reg button;
|
|
||||||
wire led;
|
|
||||||
|
|
||||||
toplevel m_toplevel(
|
|
||||||
.clk(clk),
|
|
||||||
.reset_n(reset_n),
|
|
||||||
|
|
||||||
.button(button),
|
|
||||||
.led(led),
|
|
||||||
|
|
||||||
.adc1_A(1'b0),
|
|
||||||
.adc1_B(1'b0)
|
|
||||||
);
|
|
||||||
|
|
||||||
initial begin
|
|
||||||
$dumpfile("toplevel_tb.vcd");
|
|
||||||
$dumpvars (0, toplevel_tb);
|
|
||||||
|
|
||||||
clk <= 1'b0;
|
|
||||||
reset_n <= 1'b0;
|
|
||||||
button <= 1'b0;
|
|
||||||
|
|
||||||
#50 reset_n <= 1'b1;
|
|
||||||
|
|
||||||
// Wait for clk 120 starts
|
|
||||||
@(posedge toplevel_tb.m_toplevel.clk_120);
|
|
||||||
|
|
||||||
#78 button <= 1'b1;
|
|
||||||
|
|
||||||
#185 button <= 1'b0;
|
|
||||||
|
|
||||||
#2000000
|
|
||||||
$finish;
|
|
||||||
end
|
|
||||||
|
|
||||||
always #18.5 clk = ~clk;
|
|
||||||
|
|
||||||
// Simulation stuff
|
|
||||||
// PLL quickstart
|
|
||||||
`ifndef TIMING_SIM
|
|
||||||
reg tb_pll_clk = 1'b0;
|
|
||||||
always #4.15 tb_pll_clk = ~tb_pll_clk;
|
|
||||||
initial begin
|
|
||||||
@(posedge reset_n);
|
|
||||||
repeat (8) @(posedge clk); // give the model time to measure CLKIN
|
|
||||||
force toplevel_tb.m_toplevel.m_pll.pllvr_inst.LOCK = 1'b1;
|
|
||||||
force toplevel_tb.m_toplevel.m_pll.pllvr_inst.CLKOUT = tb_pll_clk;
|
|
||||||
force toplevel_tb.m_toplevel.m_pll.pllvr_inst.CLKOUTP = tb_pll_clk;
|
|
||||||
force toplevel_tb.m_toplevel.m_pll.pllvr_inst.RESET = 1'b1;
|
|
||||||
end
|
|
||||||
`endif
|
|
||||||
// SDF annotation
|
|
||||||
`ifdef TIMING_SIM
|
|
||||||
initial begin
|
|
||||||
$sdf_annotate("impl/pnr/modem.sdf", m_toplevel, , , "MAXIMUM");
|
|
||||||
end
|
|
||||||
`endif
|
|
||||||
|
|
||||||
endmodule
|
|
||||||
27
boards/mimas_v1/constraints.ucf
Normal file
27
boards/mimas_v1/constraints.ucf
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
# Main clock input
|
||||||
|
NET "aclk" LOC = P126;
|
||||||
|
NET "aclk" TNM_NET = "SYS_CLK_PIN";
|
||||||
|
TIMESPEC TS_SYS_CLK_PIN = PERIOD "SYS_CLK_PIN" 10 ns HIGH 50 %;
|
||||||
|
|
||||||
|
# Boards button row
|
||||||
|
NET "aresetn" LOC = P120;
|
||||||
|
NET "aresetn" IOSTANDARD = LVCMOS33;
|
||||||
|
NET "aresetn" PULLUP;
|
||||||
|
|
||||||
|
NET "led_green" LOC = P29;
|
||||||
|
NET "led_green" IOSTANDARD = LVCMOS33;
|
||||||
|
NET "led_red" LOC = P26;
|
||||||
|
NET "led_red" IOSTANDARD = LVCMOS33;
|
||||||
|
|
||||||
|
NET "r2r[0]" LOC = P131;
|
||||||
|
NET "r2r[1]" LOC = P133;
|
||||||
|
NET "r2r[2]" LOC = P137;
|
||||||
|
NET "r2r[3]" LOC = P139;
|
||||||
|
NET "r2r[4]" LOC = P141;
|
||||||
|
NET "r2r[5]" LOC = P1;
|
||||||
|
NET "r2r[0]" IOSTANDARD = LVCMOS33;
|
||||||
|
NET "r2r[1]" IOSTANDARD = LVCMOS33;
|
||||||
|
NET "r2r[2]" IOSTANDARD = LVCMOS33;
|
||||||
|
NET "r2r[3]" IOSTANDARD = LVCMOS33;
|
||||||
|
NET "r2r[4]" IOSTANDARD = LVCMOS33;
|
||||||
|
NET "r2r[5]" IOSTANDARD = LVCMOS33;
|
||||||
15
modem.gprj
15
modem.gprj
@@ -1,15 +0,0 @@
|
|||||||
<?xml version="1" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE gowin-fpga-project>
|
|
||||||
<Project>
|
|
||||||
<Template>FPGA</Template>
|
|
||||||
<Version>5</Version>
|
|
||||||
<Device name="GW1NSR-4C" pn="GW1NSR-LV4CQN48PC7/I6">gw1nsr4c-009</Device>
|
|
||||||
<FileList>
|
|
||||||
<File path="HW/sigmadelta_sampler.v" type="file.verilog" enable="1"/>
|
|
||||||
<File path="HW/toplevel.v" type="file.verilog" enable="1"/>
|
|
||||||
<File path="IP/gw_clkdiv8/gw_clkdiv8.v" type="file.verilog" enable="1"/>
|
|
||||||
<File path="IP/gw_pllvr/gw_pllvr.v" type="file.verilog" enable="1"/>
|
|
||||||
<File path="CON/io.cst" type="file.cst" enable="1"/>
|
|
||||||
<File path="CON/timing.sdc" type="file.sdc" enable="1"/>
|
|
||||||
</FileList>
|
|
||||||
</Project>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<?xml version="1" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE ProjectUserData>
|
|
||||||
<UserConfig>
|
|
||||||
<Version>1.0</Version>
|
|
||||||
<FlowState>
|
|
||||||
<Process ID="Synthesis" State="2"/>
|
|
||||||
<Process ID="Pnr" State="2"/>
|
|
||||||
<Process ID="Gao" State="2"/>
|
|
||||||
<Process ID="Rtl_Gao" State="2"/>
|
|
||||||
<Process ID="Gvio" State="0"/>
|
|
||||||
<Process ID="Place" State="2"/>
|
|
||||||
</FlowState>
|
|
||||||
<ResultFileList>
|
|
||||||
<ResultFile ResultFileType="RES.netlist" ResultFilePath="impl/gwsynthesis/modem.vg"/>
|
|
||||||
<ResultFile ResultFileType="RES.pnr.bitstream" ResultFilePath="impl/pnr/modem.fs"/>
|
|
||||||
<ResultFile ResultFileType="RES.pnr.pin.rpt" ResultFilePath="impl/pnr/modem.pin.html"/>
|
|
||||||
<ResultFile ResultFileType="RES.pnr.posp.bin" ResultFilePath="impl/pnr/modem.db"/>
|
|
||||||
<ResultFile ResultFileType="RES.pnr.pwr.rpt" ResultFilePath="impl/pnr/modem.power.html"/>
|
|
||||||
<ResultFile ResultFileType="RES.pnr.report" ResultFilePath="impl/pnr/modem.rpt.html"/>
|
|
||||||
<ResultFile ResultFileType="RES.pnr.timing.paths" ResultFilePath="impl/pnr/modem.timing_paths"/>
|
|
||||||
<ResultFile ResultFileType="RES.pnr.timing.rpt" ResultFilePath="impl/pnr/modem.tr.html"/>
|
|
||||||
<ResultFile ResultFileType="RES.syn.report" ResultFilePath="impl/gwsynthesis/modem_syn.rpt.html"/>
|
|
||||||
<ResultFile ResultFileType="RES.syn.resource" ResultFilePath="impl/gwsynthesis/modem_syn_rsc.xml"/>
|
|
||||||
</ResultFileList>
|
|
||||||
<Ui>000000ff00000001fd0000000200000000000001000000029ffc0200000001fc000000360000029f0000000000fffffffaffffffff0200000003fb00000030004600700067006100500072006f006a006500630074002e00500061006e0065006c002e00440065007300690067006e0100000000ffffffff0000000000000000fb00000032004600700067006100500072006f006a006500630074002e00500061006e0065006c002e00500072006f00630065007300730100000000ffffffff0000000000000000fb00000036004600700067006100500072006f006a006500630074002e00500061006e0065006c002e0048006900650072006100720063006800790100000000ffffffff000000000000000000000003000004f60000010afc0100000001fc00000000000004f6000000a100fffffffa000000000100000002fb00000032004600700067006100500072006f006a006500630074002e00500061006e0065006c002e00470065006e006500720061006c0100000000ffffffff0000004c00fffffffb0000002e004600700067006100500072006f006a006500630074002e00500061006e0065006c002e004900730073007500650100000000ffffffff000000a100ffffff000003f00000029f00000004000000040000000800000008fc000000010000000200000004000000220043006f00720065002e0054006f006f006c006200610072002e00460069006c00650100000000ffffffff0000000000000000000000220043006f00720065002e0054006f006f006c006200610072002e004500640069007401000000a8ffffffff0000000000000000000000240043006f00720065002e0054006f006f006c006200610072002e0054006f006f006c00730100000174ffffffff0000000000000000000000280043006f00720065002e0054006f006f006c006200610072002e00500072006f0063006500730073010000024fffffffff0000000000000000</Ui>
|
|
||||||
<FpUi>312e30313131000000ff00000000fd00000002000000000000010000000193fc0200000001fc00000039000001930000008401000018fa000000010200000002fb0000001c0044006f0063006b00650072002e00530075006d006d0061007200790100000000ffffffff0000006b00fffffffb0000001c0044006f0063006b00650072002e004e00650074006c0069007300740100000000ffffffff0000005d00ffffff0000000300000500000000fefc0100000001fc00000000000005000000007b00fffffffa00000001010000000bfb0000001c0044006f0063006b00650072002e004d0065007300730061006700650100000000ffffffff0000005c00fffffffb0000002c0044006f0063006b00650072002e0049002f004f002e0043006f006e00730074007200610069006e007400730100000000ffffffff0000004a00fffffffb000000380044006f0063006b00650072002e005000720069006d00690074006900760065002e0043006f006e00730074007200610069006e007400730100000000ffffffff0000004a00fffffffb000000300044006f0063006b00650072002e00470072006f00750070002e0043006f006e00730074007200610069006e007400730100000000ffffffff0000004a00fffffffb000000360044006f0063006b00650072002e005200650073006f0075007200630065002e005200650073006500720076006100740069006f006e0100000000ffffffff0000004a00fffffffb000000380044006f0063006b00650072002e0043006c006f0063006b002e004e00650074002e0043006f006e00730074007200610069006e007400730100000000ffffffff0000004a00fffffffb000000420044006f0063006b00650072002e00470043004c004b002e005000720069006d00690074006900760065002e0043006f006e00730074007200610069006e007400730100000000ffffffff0000004a00fffffffb000000420044006f0063006b00650072002e00480043004c004b002e005000720069006d00690074006900760065002e0043006f006e00730074007200610069006e007400730100000000ffffffff0000004a00fffffffb000000440044006f0063006b00650072002e00470043004c004b0032002e005000720069006d00690074006900760065002e0043006f006e00730074007200610069006e007400730000000000ffffffff0000004a00fffffffb000000460044006f0063006b00650072002e00480043004c004b00350041002e005000720069006d00690074006900760065002e0043006f006e00730074007200610069006e007400730000000000ffffffff0000004a00fffffffb0000002e0044006f0063006b00650072002e0056007200650066002e0043006f006e00730074007200610069006e007400730100000000ffffffff0000004a00ffffff000003fa0000019300000004000000040000000800000008fc000000010000000200000001000000180054006f006f006c004200610072002e00460069006c00650100000000ffffffff0000000000000000</FpUi>
|
|
||||||
</UserConfig>
|
|
||||||
45
project.cfg
Normal file
45
project.cfg
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
[project]
|
||||||
|
name = modem
|
||||||
|
version = 0.1
|
||||||
|
out_dir = out
|
||||||
|
build_dir = build
|
||||||
|
|
||||||
|
[server]
|
||||||
|
hostname = localhost
|
||||||
|
port = 2020
|
||||||
|
privkey = /home/joppe/.ssh/id_rsa
|
||||||
|
pubkey = /home/joppe/.ssh/id_rsa.pub
|
||||||
|
|
||||||
|
[target.synth]
|
||||||
|
toolchain = ISE
|
||||||
|
# Toolchain settings
|
||||||
|
family = spartan6
|
||||||
|
device = xc6slx9
|
||||||
|
package = tqg144
|
||||||
|
speedgrade = -2
|
||||||
|
toplevel = top_generic
|
||||||
|
xst_opts = -vlgincdir rtl
|
||||||
|
#ngdbuild_opts =
|
||||||
|
#map_opts =
|
||||||
|
#par_opts =
|
||||||
|
#netgen_opts =
|
||||||
|
#bitgen_opts =
|
||||||
|
#trce_opts =
|
||||||
|
# Files
|
||||||
|
#files_vhdl =
|
||||||
|
files_verilog = rtl/toplevel/top_generic.v
|
||||||
|
rtl/core/nco_q15.v
|
||||||
|
files_con = boards/mimas_v1/constraints.ucf
|
||||||
|
files_other = rtl/core/nco_q15_funcs.vh
|
||||||
|
|
||||||
|
[target.sim]
|
||||||
|
toolchain = iverilog
|
||||||
|
runtime = all
|
||||||
|
toplevel = tb_nco_q15
|
||||||
|
ivl_opts = -Irtl
|
||||||
|
#vvp_opts =
|
||||||
|
# Files
|
||||||
|
#files_sysverilog =
|
||||||
|
files_verilog = sim/tb/tb_nco_q15.v
|
||||||
|
rtl/core/nco_q15.v
|
||||||
|
files_other = rtl/core/nco_q15_funcs.vh
|
||||||
176
rtl/core/nco_q15.v
Normal file
176
rtl/core/nco_q15.v
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// nco_q15.v
|
||||||
|
// Tiny DDS/NCO @ FS_HZ sample rate with Q1.15 sine/cos outputs
|
||||||
|
// - Phase accumulator width: PHASE_BITS (default 32)
|
||||||
|
// - Quarter-wave LUT: 2^QTR_ADDR_BITS entries (default 64)
|
||||||
|
// - Clock domain: CLK_HZ (default 120 MHz), creates 1-cycle strobe at FS_HZ
|
||||||
|
// - Frequency control: write 'ftw' (32-bit tuning word)
|
||||||
|
// FTW = round(f_out * 2^PHASE_BITS / FS_HZ)
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
|
||||||
|
module nco_q15 #
|
||||||
|
(
|
||||||
|
// -------- Synth parameters --------
|
||||||
|
parameter integer PHASE_BITS = 32, // accumulator width
|
||||||
|
parameter integer QTR_ADDR_BITS = 6, // 64-entry quarter-wave LUT
|
||||||
|
parameter integer CLK_HZ = 120_000_000, // input clock (Hz)
|
||||||
|
parameter integer FS_HZ = 40_000 // output sample rate (Hz)
|
||||||
|
)
|
||||||
|
(
|
||||||
|
input wire clk, // CLK_HZ domain
|
||||||
|
input wire rst_n, // async active-low reset
|
||||||
|
|
||||||
|
// Frequency control
|
||||||
|
input wire [31:0] ftw_in, // Frequency Tuning Word (FTW)
|
||||||
|
input wire ftw_we, // write-enable strobe (1 clk pulse)
|
||||||
|
|
||||||
|
// Outputs (valid on clk_en rising pulse, i.e., at FS_HZ)
|
||||||
|
output reg signed [15:0] sin_q15, // signed Q1.15 sine
|
||||||
|
output reg signed [15:0] cos_q15, // signed Q1.15 cosine
|
||||||
|
output reg clk_en // 1-cycle strobe @ FS_HZ
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "core/nco_q15_funcs.vh"
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Sample-rate enable: divide CLK_HZ down to FS_HZ
|
||||||
|
// - DIV must be an integer (CLK_HZ / FS_HZ).
|
||||||
|
// - clk_en goes high for exactly 1 clk cycle every DIV cycles.
|
||||||
|
// ==========================================================
|
||||||
|
localparam integer DIV = CLK_HZ / FS_HZ;
|
||||||
|
|
||||||
|
// Optional safety for misconfiguration (ignored by synthesis tools):
|
||||||
|
initial if (CLK_HZ % FS_HZ != 0)
|
||||||
|
$display("WARNING nco_q15: CLK_HZ (%0d) not divisible by FS_HZ (%0d).", CLK_HZ, FS_HZ);
|
||||||
|
|
||||||
|
// Counter width: enough bits to count to DIV-1 (use a generous fixed width to keep 2001-compatible)
|
||||||
|
// If you prefer, replace 16 with $clog2(DIV) on a tool that supports it well.
|
||||||
|
reg [15:0] tick_cnt;
|
||||||
|
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
tick_cnt <= 16'd0;
|
||||||
|
clk_en <= 1'b0;
|
||||||
|
end else begin
|
||||||
|
if (tick_cnt == DIV-1) begin
|
||||||
|
tick_cnt <= 16'd0;
|
||||||
|
clk_en <= 1'b1; // 1-cycle pulse
|
||||||
|
end else begin
|
||||||
|
tick_cnt <= tick_cnt + 16'd1;
|
||||||
|
clk_en <= 1'b0;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Frequency control register
|
||||||
|
// - You present ftw_in and pulse ftw_we (1 clk) to update.
|
||||||
|
// ==========================================================
|
||||||
|
reg [31:0] ftw;
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) ftw <= ftw_from_hz(1000, PHASE_BITS, FS_HZ); // Start at 1khz
|
||||||
|
else if (ftw_we) ftw <= ftw_in;
|
||||||
|
end
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Phase accumulators
|
||||||
|
// - phase_sin advances by FTW once per sample (on clk_en).
|
||||||
|
// - cosine is generated by a +90° phase lead (π/2), i.e., add 2^(PHASE_BITS-2).
|
||||||
|
// Here we realize it by deriving phase_cos from phase_sin each sample.
|
||||||
|
// ==========================================================
|
||||||
|
reg [PHASE_BITS-1:0] phase_sin, phase_cos;
|
||||||
|
wire [PHASE_BITS-1:0] phase_cos_plus90 = phase_sin + ({{(PHASE_BITS-2){1'b0}}, 2'b01} << (PHASE_BITS-2)); // +90°
|
||||||
|
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
phase_sin <= {PHASE_BITS{1'b0}};
|
||||||
|
phase_cos <= {PHASE_BITS{1'b0}};
|
||||||
|
end else if (clk_en) begin
|
||||||
|
phase_sin <= phase_sin + ftw;
|
||||||
|
// Keep cosine aligned to the same sample using a +90° offset from updated phase_sin
|
||||||
|
phase_cos <= phase_cos_plus90 + ftw;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Phase -> quadrant/index
|
||||||
|
// - q_*: top 2 bits select quadrant (0..3).
|
||||||
|
// - idx_*_raw: next QTR_ADDR_BITS select a point in 0..π/2.
|
||||||
|
// - For odd quadrants, mirror the LUT index.
|
||||||
|
// ==========================================================
|
||||||
|
wire [1:0] q_sin = phase_sin[PHASE_BITS-1 -: 2];
|
||||||
|
wire [1:0] q_cos = phase_cos[PHASE_BITS-1 -: 2];
|
||||||
|
|
||||||
|
wire [QTR_ADDR_BITS-1:0] idx_sin_raw = phase_sin[PHASE_BITS-3 -: QTR_ADDR_BITS];
|
||||||
|
wire [QTR_ADDR_BITS-1:0] idx_cos_raw = phase_cos[PHASE_BITS-3 -: QTR_ADDR_BITS];
|
||||||
|
|
||||||
|
wire [QTR_ADDR_BITS-1:0] idx_sin = (q_sin[0]) ? ({QTR_ADDR_BITS{1'b1}} - idx_sin_raw) : idx_sin_raw;
|
||||||
|
wire [QTR_ADDR_BITS-1:0] idx_cos = (q_cos[0]) ? ({QTR_ADDR_BITS{1'b1}} - idx_cos_raw) : idx_cos_raw;
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Quarter-wave 8-bit LUT (0..255). 64 entries map 0..π/2.
|
||||||
|
// ==========================================================
|
||||||
|
wire [7:0] lut_sin_mag, lut_cos_mag;
|
||||||
|
sine_qtr_lut64 u_lut_s (.addr(idx_sin), .dout(lut_sin_mag));
|
||||||
|
sine_qtr_lut64 u_lut_c (.addr(idx_cos), .dout(lut_cos_mag));
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Sign & scale to Q1.15
|
||||||
|
// - Scale: <<7 so 255 becomes 32640 (slightly below 32767).
|
||||||
|
// - Apply sign by quadrant: quadrants 2 & 3 are negative.
|
||||||
|
// ==========================================================
|
||||||
|
wire signed [15:0] sin_mag_q15 = {1'b0, lut_sin_mag, 7'd0};
|
||||||
|
wire signed [15:0] cos_mag_q15 = {1'b0, lut_cos_mag, 7'd0};
|
||||||
|
|
||||||
|
wire sin_neg = (q_sin >= 2); // quadrants 2,3
|
||||||
|
wire cos_neg = (q_cos >= 2);
|
||||||
|
|
||||||
|
wire signed [15:0] sin_q15_next = sin_neg ? -sin_mag_q15 : sin_mag_q15;
|
||||||
|
wire signed [15:0] cos_q15_next = cos_neg ? -cos_mag_q15 : cos_mag_q15;
|
||||||
|
|
||||||
|
// ==========================================================
|
||||||
|
// Output registers (update on clk_en)
|
||||||
|
// ==========================================================
|
||||||
|
always @(posedge clk or negedge rst_n) begin
|
||||||
|
if (!rst_n) begin
|
||||||
|
sin_q15 <= 16'sd0;
|
||||||
|
cos_q15 <= 16'sd0;
|
||||||
|
end else if (clk_en) begin
|
||||||
|
sin_q15 <= sin_q15_next;
|
||||||
|
cos_q15 <= cos_q15_next;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
// 64-entry quarter-wave sine ROM (8-bit), indices 0..63 map 0..π/2.
|
||||||
|
// Plain Verilog case ROM. You can regenerate these with a script
|
||||||
|
// or replace with a vendor-specific ROM for BRAM inference.
|
||||||
|
// ------------------------------------------------------------
|
||||||
|
module sine_qtr_lut64(
|
||||||
|
input wire [5:0] addr,
|
||||||
|
output reg [7:0] dout
|
||||||
|
);
|
||||||
|
always @* begin
|
||||||
|
case (addr)
|
||||||
|
6'd0: dout = 8'd0; 6'd1: dout = 8'd6; 6'd2: dout = 8'd13; 6'd3: dout = 8'd19;
|
||||||
|
6'd4: dout = 8'd25; 6'd5: dout = 8'd31; 6'd6: dout = 8'd37; 6'd7: dout = 8'd44;
|
||||||
|
6'd8: dout = 8'd50; 6'd9: dout = 8'd56; 6'd10: dout = 8'd62; 6'd11: dout = 8'd68;
|
||||||
|
6'd12: dout = 8'd74; 6'd13: dout = 8'd80; 6'd14: dout = 8'd86; 6'd15: dout = 8'd92;
|
||||||
|
6'd16: dout = 8'd98; 6'd17: dout = 8'd103; 6'd18: dout = 8'd109; 6'd19: dout = 8'd115;
|
||||||
|
6'd20: dout = 8'd120; 6'd21: dout = 8'd126; 6'd22: dout = 8'd131; 6'd23: dout = 8'd136;
|
||||||
|
6'd24: dout = 8'd142; 6'd25: dout = 8'd147; 6'd26: dout = 8'd152; 6'd27: dout = 8'd157;
|
||||||
|
6'd28: dout = 8'd162; 6'd29: dout = 8'd167; 6'd30: dout = 8'd171; 6'd31: dout = 8'd176;
|
||||||
|
6'd32: dout = 8'd180; 6'd33: dout = 8'd185; 6'd34: dout = 8'd189; 6'd35: dout = 8'd193;
|
||||||
|
6'd36: dout = 8'd197; 6'd37: dout = 8'd201; 6'd38: dout = 8'd205; 6'd39: dout = 8'd208;
|
||||||
|
6'd40: dout = 8'd212; 6'd41: dout = 8'd215; 6'd42: dout = 8'd219; 6'd43: dout = 8'd222;
|
||||||
|
6'd44: dout = 8'd225; 6'd45: dout = 8'd228; 6'd46: dout = 8'd231; 6'd47: dout = 8'd233;
|
||||||
|
6'd48: dout = 8'd236; 6'd49: dout = 8'd238; 6'd50: dout = 8'd240; 6'd51: dout = 8'd242;
|
||||||
|
6'd52: dout = 8'd244; 6'd53: dout = 8'd246; 6'd54: dout = 8'd247; 6'd55: dout = 8'd249;
|
||||||
|
6'd56: dout = 8'd250; 6'd57: dout = 8'd251; 6'd58: dout = 8'd252; 6'd59: dout = 8'd253;
|
||||||
|
6'd60: dout = 8'd254; 6'd61: dout = 8'd254; 6'd62: dout = 8'd255; 6'd63: dout = 8'd255;
|
||||||
|
default: dout=8'd0;
|
||||||
|
endcase
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
24
rtl/core/nco_q15_funcs.vh
Normal file
24
rtl/core/nco_q15_funcs.vh
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
// ==========================================================
|
||||||
|
// Helper: FTW function (compile-time or runtime call)
|
||||||
|
// FTW = round( f_hz * 2^PHASE_BITS / FS_HZ )
|
||||||
|
// Notes:
|
||||||
|
// - Accepts integer Hz.
|
||||||
|
// - Uses 64-bit math to avoid overflow for typical params.
|
||||||
|
// - Can be called from a testbench or combinational logic that
|
||||||
|
// prepares 'ftw_in' before asserting 'ftw_we'.
|
||||||
|
// Example:
|
||||||
|
// initial begin
|
||||||
|
// #1;
|
||||||
|
// $display("FTW 1kHz = 0x%08x", ftw_from_hz(1000));
|
||||||
|
// end
|
||||||
|
// ==========================================================
|
||||||
|
function [31:0] ftw_from_hz;
|
||||||
|
input integer f_hz;
|
||||||
|
input integer phase_bits;
|
||||||
|
input integer fs_hz;
|
||||||
|
reg [63:0] numer;
|
||||||
|
begin
|
||||||
|
numer = ((64'd1 << phase_bits) * f_hz) + (fs_hz/2);
|
||||||
|
ftw_from_hz = numer / fs_hz;
|
||||||
|
end
|
||||||
|
endfunction
|
||||||
45
rtl/toplevel/top_generic.v
Normal file
45
rtl/toplevel/top_generic.v
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module top_generic(
|
||||||
|
input wire aclk,
|
||||||
|
input wire aresetn,
|
||||||
|
|
||||||
|
output wire led_green,
|
||||||
|
output wire led_red,
|
||||||
|
|
||||||
|
output wire[5:0] r2r
|
||||||
|
);
|
||||||
|
|
||||||
|
`include "core/nco_q15_funcs.vh"
|
||||||
|
|
||||||
|
assign led_green = 1'b0;
|
||||||
|
assign led_red = 1'b0;
|
||||||
|
|
||||||
|
wire [15:0] sin_q15;
|
||||||
|
wire clk_en;
|
||||||
|
nco_q15 #(
|
||||||
|
.CLK_HZ(100_000_000),
|
||||||
|
.PHASE_BITS(16)
|
||||||
|
) nco (
|
||||||
|
.clk (aclk),
|
||||||
|
.rst_n (aresetn),
|
||||||
|
.ftw_in (32'h0),
|
||||||
|
.ftw_we (1'b0),
|
||||||
|
.sin_q15(sin_q15),
|
||||||
|
.cos_q15(),
|
||||||
|
.clk_en (clk_en)
|
||||||
|
);
|
||||||
|
|
||||||
|
// sin_q15: signed Q15 in [-32768, +32767]
|
||||||
|
wire signed [15:0] s = sin_q15;
|
||||||
|
// Bias to 0..65535 and round before downscaling by 1024 (>>10)
|
||||||
|
wire [16:0] biased = s + 17'sd32768; // 0..65535
|
||||||
|
wire [5:0] dac_code_next = biased[15:10]; // 0..63 (MSB=bit5)
|
||||||
|
// Register it at the sample rate (clk_en)
|
||||||
|
reg [5:0] dac_code;
|
||||||
|
always @(posedge aclk or negedge aresetn) begin
|
||||||
|
if (!aresetn) dac_code <= 6'd0;
|
||||||
|
else if (clk_en) dac_code <= dac_code_next;
|
||||||
|
end
|
||||||
|
assign r2r = dac_code;
|
||||||
|
endmodule
|
||||||
21
scripts/gen_sin_lut.py
Normal file
21
scripts/gen_sin_lut.py
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import math
|
||||||
|
|
||||||
|
# for i in range(0, 64, 4):
|
||||||
|
# for j in range(4):
|
||||||
|
# val = math.sin((i+j) * math.pi/256)
|
||||||
|
# val = int(val*256*1.4)
|
||||||
|
# print(f"6'd{i+j}: dout=8'd{val}; ", end='')
|
||||||
|
# print('')
|
||||||
|
|
||||||
|
|
||||||
|
N = 64 # quarter-wave samples
|
||||||
|
AMP = 255 # 8-bit max
|
||||||
|
for i in range(0, N, 4):
|
||||||
|
line = []
|
||||||
|
for j in range(4):
|
||||||
|
k = i + j
|
||||||
|
theta = k * math.pi / (2*N) # 0 .. (π/2)*(N-1)/N ; never hits 1.0
|
||||||
|
val = int(math.floor(AMP * math.sin(theta) + 0.5)) # rounded, stays <=254
|
||||||
|
if val > 255: val = 255
|
||||||
|
line.append(f"6'd{k}: dout = 8'd{val};")
|
||||||
|
print(' '.join(line))
|
||||||
6
scripts/planahead.tcl
Normal file
6
scripts/planahead.tcl
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
create_project project_1 /tmp/project_1 -part xc6slx9tqg144-2
|
||||||
|
set_property design_mode GateLvl [current_fileset]
|
||||||
|
add_files -norecurse /data/joppe/projects/modem/out/synth/synth.ngc
|
||||||
|
import_files -force -norecurse
|
||||||
|
import_files -fileset constrs_1 -force -norecurse /data/joppe/projects/modem/boards/mimas_v1/constraints.ucf
|
||||||
|
import_as_run -run impl_1 -twx /data/joppe/projects/modem/out/synth/timing.twx /data/joppe/projects/modem/out/synth/synth.ncd
|
||||||
@@ -1 +0,0 @@
|
|||||||
openFPGALoader impl/pnr/modem.fs
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
gowin_sh scripts/run_all.tcl
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
open_project modem.gprj
|
|
||||||
run all
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
cvc \
|
|
||||||
+define+TIMING_SIM \
|
|
||||||
+acc \
|
|
||||||
+sdf_noerrors +sdf_nowarns \
|
|
||||||
+show_canceled_e +suppress_warns+653+3102 \
|
|
||||||
-v SIM/prim_tsim.v \
|
|
||||||
impl/pnr/modem.vo \
|
|
||||||
SIM/toplevel_tb.v \
|
|
||||||
SIM/globals.v \
|
|
||||||
+sdf_annotate+impl/pnr/modem.sdf+toplevel_tb.m_toplevel \
|
|
||||||
+maxdelays \
|
|
||||||
+librescan
|
|
||||||
./cvcsim
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# iverilog -o toplevel_tb.vp \
|
|
||||||
# /opt/packages/gowin/IDE/simlib/gw1n/prim_sim.v \
|
|
||||||
# HW/toplevel.v \
|
|
||||||
# SIM/toplevel_tb.v
|
|
||||||
# vvp toplevel_tb.vp
|
|
||||||
|
|
||||||
cvc +acc \
|
|
||||||
+show_canceled_e +suppress_warns+653+3102 \
|
|
||||||
+define+FAST_PLL_SIM \
|
|
||||||
-v SIM/prim_tsim.v \
|
|
||||||
\
|
|
||||||
IP/gw_pllvr/gw_pllvr.v \
|
|
||||||
IP/gw_clkdiv8/gw_clkdiv8.v \
|
|
||||||
HW/toplevel.v \
|
|
||||||
HW/sampling.v \
|
|
||||||
\
|
|
||||||
SIM/sigmadelta_sampler.v \
|
|
||||||
\
|
|
||||||
SIM/sampling_tb.v \
|
|
||||||
SIM/globals.v \
|
|
||||||
+librescan
|
|
||||||
./cvcsim
|
|
||||||
# SIM/toplevel_tb.v \
|
|
||||||
50
sim/tb/tb_nco_q15.v
Normal file
50
sim/tb/tb_nco_q15.v
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
`timescale 1ns/1ps
|
||||||
|
|
||||||
|
module tb_nco_q15();
|
||||||
|
`include "core/nco_q15_funcs.vh"
|
||||||
|
|
||||||
|
// Clock and reset generation
|
||||||
|
reg clk;
|
||||||
|
reg resetn;
|
||||||
|
initial clk <= 1'b0;
|
||||||
|
initial resetn <= 1'b0;
|
||||||
|
always #4.17 clk <= !clk;
|
||||||
|
initial #40 resetn <= 1'b1;
|
||||||
|
|
||||||
|
// Default run
|
||||||
|
initial begin
|
||||||
|
$dumpfile("out.vcd");
|
||||||
|
$dumpvars;
|
||||||
|
#5_000_000
|
||||||
|
$finish;
|
||||||
|
end;
|
||||||
|
|
||||||
|
|
||||||
|
reg [31:0] ftw_in;
|
||||||
|
reg ftw_we;
|
||||||
|
wire [15:0] sin_q15;
|
||||||
|
wire [15:0] cos_q15;
|
||||||
|
wire out_en;
|
||||||
|
|
||||||
|
nco_q15 #(.PHASE_BITS(16)) nco (
|
||||||
|
.clk (clk),
|
||||||
|
.rst_n (resetn),
|
||||||
|
.ftw_in (ftw_in),
|
||||||
|
.ftw_we (ftw_we),
|
||||||
|
.sin_q15(sin_q15),
|
||||||
|
.cos_q15(cos_q15),
|
||||||
|
.clk_en (out_en)
|
||||||
|
);
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
ftw_in = 32'h0;
|
||||||
|
ftw_we = 1'b0;
|
||||||
|
#100
|
||||||
|
@(posedge clk);
|
||||||
|
ftw_in = ftw_from_hz(1000, 16, 40000);
|
||||||
|
ftw_we = 1'b1;
|
||||||
|
@(posedge clk);
|
||||||
|
ftw_we = 1'b0;
|
||||||
|
end;
|
||||||
|
|
||||||
|
endmodule
|
||||||
Reference in New Issue
Block a user