add Tang nano 9K dual 12 bit ADC project

This commit is contained in:
Steve Markgraf 2025-03-24 23:36:17 +01:00
parent a9bc725209
commit 1339c06b11
6 changed files with 336 additions and 10 deletions

29
common/div3.v Normal file
View file

@ -0,0 +1,29 @@
module clk_div3(clk,reset, clk_out);
input clk;
input reset;
output clk_out;
reg [1:0] pos_count, neg_count;
wire [1:0] r_nxt;
always @(posedge clk) begin
if (reset)
pos_count <= 0;
else if (pos_count == 2)
pos_count <= 0;
else
pos_count <= pos_count + 1;
end
always @(negedge clk) begin
if (reset)
neg_count <=0;
else if (neg_count == 2)
neg_count <= 0;
else
neg_count<= neg_count +1;
end
assign clk_out = ~((pos_count == 2) | (neg_count == 2));
endmodule

View file

@ -14,8 +14,8 @@ module hsdaoh_core
input wire clk_pixel,
input wire fifo_empty,
input wire fifo_aempty,
output reg fifo_read_en,
input wire [15:0] data_in
output wire fifo_read_en,
input wire [23:0] data_in
);
parameter USE_CRC = 1;
@ -48,6 +48,13 @@ module hsdaoh_core
wire [11:0] screen_width;
wire [10:0] screen_height;
reg read_en;
reg [15:0] tenbit_data;
reg [2:0] pack_state;
// For 10 bit mode, disable FIFO every four 20 bit words so we can pack them to 16 bit
assign fifo_read_en = read_en && (pack_state != 1);
always @(posedge clk_pixel) begin
if (cy < screen_height) begin
@ -68,9 +75,29 @@ always @(posedge clk_pixel) begin
hdmi_data <= {last_line_crc, 8'h00};
end else if (cx < screen_width) begin
if (fifo_read_en && !fifo_empty) begin
if (read_en && !fifo_empty) begin
// regular output of FIFO data
hdmi_data <= {data_in[15:0], 8'h00};
hdmi_data <= (pack_state == 2) ? {tenbit_data[15:0], 8'h00} : {data_in[11:4], data_in[23:16], 8'h00};
// packing of 24 bit to 16 bit
case (pack_state)
0:
begin
tenbit_data[7:0] <= {data_in[15:12], data_in[3:0]};
pack_state <= 1;
end
1:
begin
tenbit_data[15:8] <= {data_in[15:12], data_in[3:0]};
pack_state <= 2;
end
2:
begin
pack_state <= 0;
end
default:
pack_state <= 0;
endcase
// increment line payload counter
line_word_cnt <= line_word_cnt + 1'b1;
@ -79,7 +106,7 @@ always @(posedge clk_pixel) begin
hdmi_data <= {idle_counter[15:8], idle_counter[7:0], 8'h00};
// increment idle counter
idle_counter <= idle_counter + 1'b1;
// idle_counter <= idle_counter + 1'b1;
end
end else
line_word_cnt <= 16'h0000;
@ -87,22 +114,22 @@ always @(posedge clk_pixel) begin
// Enable reading before beginning of next line
if ((cx == frame_width-1) && (cy != screen_height-1)) begin
if (!fifo_empty)
fifo_read_en = 1'b1;
read_en = 1'b1;
end
// switch read off at end of line before sending the word counter
// -2 because the last word is reserved (line_word_cnt and metadata)
if (cx == screen_width-2-USE_CRC)
fifo_read_en = 1'b0;
read_en = 1'b0;
end
// switch read off during blanking
if (cy > screen_height)
fifo_read_en = 1'b0;
read_en = 1'b0;
// switch read off when FIFO has only one word remaining
if (fifo_aempty)
fifo_read_en = 1'b0;
read_en = 1'b0;
// increment the frame counter at the end of the frame
if ((cx == frame_width-1) && (cy == frame_height-1)) begin
@ -111,7 +138,15 @@ always @(posedge clk_pixel) begin
// start FIFO readout
if (!fifo_empty)
fifo_read_en = 1'b1;
read_en = 1'b1;
end
// in order to align the packed data, stop the FIFO readout if we cannot complete a packing cycle
// at the end of the frame. This ensures that each frame starts with pack_state 0
// FIXME: only done at beginning of last line, should be removed
if ((cy == screen_height-1) && read_en) begin
if (pack_state == 2)
read_en = 1'b0;
end
if (cx == 0) begin
@ -129,6 +164,9 @@ always @(posedge clk_pixel) begin
10 : status_nibble <= frame_cnt[11:8];
11 : status_nibble <= frame_cnt[15:12];
14 : status_nibble <= { 3'b000, USE_CRC };
92 : status_nibble <= 4'b0010; // set format ID = FPGA_12BIT_DUAL
93 : status_nibble <= 4'b0001;
94 : status_nibble <= 4'b0001;
default : status_nibble <= 4'h0;
endcase
end

View file

@ -0,0 +1,27 @@
<?xml version="1" encoding="UTF-8"?>
<!DOCTYPE gowin-fpga-project>
<Project>
<Template>FPGA</Template>
<Version>5</Version>
<Device name="GW1NR-9C" pn="GW1NR-LV9QN88PC6/I5">gw1nr9c-004</Device>
<FileList>
<File path="common/async_fifo/async_fifo.v" type="file.verilog" enable="1"/>
<File path="common/async_fifo/fifomem.v" type="file.verilog" enable="1"/>
<File path="common/async_fifo/rptr_empty.v" type="file.verilog" enable="1"/>
<File path="common/async_fifo/sync_r2w.v" type="file.verilog" enable="1"/>
<File path="common/async_fifo/sync_w2r.v" type="file.verilog" enable="1"/>
<File path="common/async_fifo/wptr_full.v" type="file.verilog" enable="1"/>
<File path="common/div3.v" type="file.verilog" enable="1"/>
<File path="common/hdmi/auxiliary_video_information_info_frame.v" type="file.verilog" enable="1"/>
<File path="common/hdmi/hdmi.v" type="file.verilog" enable="1"/>
<File path="common/hdmi/packet_assembler.v" type="file.verilog" enable="1"/>
<File path="common/hdmi/packet_picker.v" type="file.verilog" enable="1"/>
<File path="common/hdmi/serializer.v" type="file.verilog" enable="1"/>
<File path="common/hdmi/tmds_channel.v" type="file.verilog" enable="1"/>
<File path="common/hsdaoh/crc16_ccitt.v" type="file.verilog" enable="1"/>
<File path="common/hsdaoh/hsdaoh_core.v" type="file.verilog" enable="1"/>
<File path="hsdaoh_nano9k_dualadc/top.v" type="file.verilog" enable="1"/>
<File path="hsdaoh_nano9k_dualadc/hsdaoh_nano9k_dualadc.cst" type="file.cst" enable="1"/>
<File path="hsdaoh_nano9k_dualadc/hsdaoh_nano9k_dualadc.sdc" type="file.sdc" enable="1"/>
</FileList>
</Project>

View file

@ -0,0 +1,65 @@
IO_LOC "sys_resetn" 4;
IO_PORT "sys_resetn" PULL_MODE=UP;
IO_LOC "sys_clk" 52;
IO_PORT "sys_clk" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "tmds_d_p[0]" 71,70;
IO_PORT "tmds_d_p[0]" PULL_MODE=NONE DRIVE=8;
IO_LOC "tmds_d_p[1]" 73,72;
IO_PORT "tmds_d_p[1]" PULL_MODE=NONE DRIVE=8;
IO_LOC "tmds_d_p[2]" 75,74;
IO_PORT "tmds_d_p[2]" PULL_MODE=NONE DRIVE=8;
IO_LOC "tmds_clk_p" 69,68;
IO_PORT "tmds_clk_p" PULL_MODE=NONE DRIVE=8;
IO_LOC "adc0_data[0]" 41;
IO_PORT "adc0_data[0]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[1]" 35;
IO_PORT "adc0_data[1]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[2]" 40;
IO_PORT "adc0_data[2]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[3]" 34;
IO_PORT "adc0_data[3]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[4]" 33;
IO_PORT "adc0_data[4]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[5]" 30;
IO_PORT "adc0_data[5]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[6]" 29;
IO_PORT "adc0_data[6]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[7]" 28;
IO_PORT "adc0_data[7]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[8]" 27;
IO_PORT "adc0_data[8]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[9]" 26;
IO_PORT "adc0_data[9]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[10]" 25;
IO_PORT "adc0_data[10]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc0_data[11]" 38;
IO_PORT "adc0_data[11]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[0]" 32;
IO_PORT "adc1_data[0]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[1]" 31;
IO_PORT "adc1_data[1]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[2]" 49;
IO_PORT "adc1_data[2]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[3]" 48;
IO_PORT "adc1_data[3]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[4]" 76;
IO_PORT "adc1_data[4]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[5]" 57;
IO_PORT "adc1_data[5]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[6]" 56;
IO_PORT "adc1_data[6]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[7]" 55;
IO_PORT "adc1_data[7]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[8]" 54;
IO_PORT "adc1_data[8]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[9]" 53;
IO_PORT "adc1_data[9]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[10]" 51;
IO_PORT "adc1_data[10]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc1_data[11]" 42;
IO_PORT "adc1_data[11]" IO_TYPE=LVCMOS33 PULL_MODE=UP;
IO_LOC "adc_clkout" 36;
IO_PORT "adc_clkout" IO_TYPE=LVCMOS33 PULL_MODE=UP;

View file

@ -0,0 +1 @@
create_clock -name sys_clk -period 37.04 [get_ports {sys_clk}] -add

166
hsdaoh_nano9k_dualadc/top.v Normal file
View file

@ -0,0 +1,166 @@
// hsdaoh - High Speed Data Acquisition over HDMI
// Dual 12 bit ADC top design for Tang Nano 9K
// Copyright (C) 2024-25 by Steve Markgraf <steve@steve-m.de>
// License: MIT
module top (
sys_clk,
sys_resetn,
tmds_clk_n,
tmds_clk_p,
tmds_d_n,
tmds_d_p,
adc0_data,
adc1_data,
adc_clkout
);
input sys_clk;
input sys_resetn;
output wire tmds_clk_n;
output wire tmds_clk_p;
output wire [2:0] tmds_d_n;
output wire [2:0] tmds_d_p;
input wire [11:0] adc0_data;
input wire [11:0] adc1_data;
output wire adc_clkout;
wire [2:0] tmds;
wire clk_pixel;
wire clk_pixel_x5;
wire hdmi_pll_lock;
wire data_pll_out;
wire clk_data;
wire data_pll_lock;
assign adc_clkout = clk_data;
localparam HDMI_PLL_IDIV = 3;
localparam HDMI_PLL_FBDIV = 46;
localparam HDMI_PLL_ODIV = 2;
// PLL for HDMI clock
rPLL #(
.FCLKIN (27),
.IDIV_SEL (HDMI_PLL_IDIV),
.FBDIV_SEL (HDMI_PLL_FBDIV),
.ODIV_SEL (HDMI_PLL_ODIV),
.DEVICE ("GW1NR-9C")
) hdmi_pll (
.CLKIN(sys_clk),
.CLKFB(1'b0),
.RESET(1'b0),
.RESET_P(1'b0),
.FBDSEL(6'b0),
.IDSEL(6'b0),
.ODSEL(6'b0),
.DUTYDA(4'b0),
.PSDA(4'b0),
.FDLY(4'b0),
.CLKOUT(clk_pixel_x5),
.LOCK(hdmi_pll_lock),
.CLKOUTP(),
.CLKOUTD(),
.CLKOUTD3()
);
CLKDIV #(
.DIV_MODE(5),
.GSREN("false")
) div_5 (
.CLKOUT(clk_pixel),
.HCLKIN(clk_pixel_x5),
.RESETN(hdmi_pll_lock),
.CALIB(1'b0)
);
// 120 MHz
localparam DATA_PLL_IDIV = 8;
localparam DATA_PLL_FBDIV = 39;
localparam DATA_PLL_ODIV = 8;
// 120 MHz / 3 = 40 MHz
clk_div3 clkdiv (.clk(data_pll_out),
.reset (!hdmi_pll_lock),
.clk_out(clk_data));
// PLL for data clock
rPLL #(
.FCLKIN (27),
.IDIV_SEL (DATA_PLL_IDIV),
.FBDIV_SEL (DATA_PLL_FBDIV),
.ODIV_SEL (DATA_PLL_ODIV),
.DEVICE ("GW1NR-9C")
) data_pll (
.CLKIN (sys_clk),
.CLKFB (1'b0),
.RESET (rst),
.RESET_P (1'b0),
.FBDSEL (6'b0),
.IDSEL (6'b0),
.ODSEL (6'b0),
.DUTYDA (4'b0),
.PSDA (4'b0),
.FDLY (4'b0),
.CLKOUT (data_pll_out),
.LOCK (data_pll_lock),
.CLKOUTP (),
.CLKOUTD (),
.CLKOUTD3 ()
);
reg [23:0] fifo_in;
wire write_enable;
wire [23:0] fifo_out;
wire fifo_empty;
wire fifo_aempty;
wire Full_o;
wire FifoHalfFull;
wire FifoFull;
wire fifo_rd_en_i;
async_fifo #(
.DSIZE(24),
.ASIZE($clog2(8192)), // 3 + (1982 * 4) = 7931 => at least 8K entries to buffer 4 lines during VSYNC
.FALLTHROUGH("FALSE")
) fifo (
.wclk(clk_data),
.wrst_n(hdmi_pll_lock),
.winc(write_enable),
.wdata(fifo_in),
.wfull(FifoFull),
.awfull(FifoHalfFull), //fixme
.rclk(clk_pixel),
.rrst_n(hdmi_pll_lock),
.rinc(fifo_rd_en_i),
.rdata(fifo_out),
.rempty(fifo_empty),
.arempty(fifo_aempty)
);
hsdaoh_core hsdaoh (
.rstn(hdmi_pll_lock),
.tmds_clk_n(tmds_clk_n),
.tmds_clk_p(tmds_clk_p),
.tmds_d_n(tmds_d_n),
.tmds_d_p(tmds_d_p),
.clk_pixel_x5(clk_pixel_x5),
.clk_pixel(clk_pixel),
.fifo_empty(fifo_empty),
.fifo_aempty(fifo_aempty),
.fifo_read_en(fifo_rd_en_i),
.data_in(fifo_out)
);
assign write_enable = 1'b1;
always @(posedge clk_data) begin
fifo_in <= {adc0_data[11:0], adc1_data[11:0]};
end
endmodule