diff --git a/common/hsdaoh/crc16_ccitt.v b/common/hsdaoh/crc16_ccitt.v
new file mode 100644
index 0000000..8d70f7e
--- /dev/null
+++ b/common/hsdaoh/crc16_ccitt.v
@@ -0,0 +1,86 @@
+/* -----------------------------------------------------------------------------------
+ * Module Name : crc16_ccitt
+ * Generated by : https://github.com/PXVI/ip_parallel_custom_crc_gerator_verilog
+ * Author : pxvi
+ * Description : Parallel CRC( 16-bit ) module with 16-bit data input.
+ * Polynomial Degrees [ 16 12 5 0 ]
+ * -----------------------------------------------------------------------------------
+ *
+ * MIT License
+ *
+ * Copyright (c) 2024 k-sva
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the Software), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ *
+ * ----------------------------------------------------------------------------------- */
+module crc16_ccitt #( parameter RESET_SEED = 'hffff )(
+ input wire CLK,
+ input wire RSTn,
+
+ input wire [15:0] data_in,
+ input wire enable,
+ input wire clear,
+
+ output [15:0] CRC
+ );
+
+
+ reg [16-1:0] Mout_p, Mout_n;
+
+ always@( posedge CLK )
+ begin
+ Mout_p <= Mout_n;
+
+ if( !RSTn )
+ begin
+ Mout_p <= RESET_SEED;
+ end
+ end
+
+ always@( * )
+ begin
+ Mout_n = Mout_p;
+
+ if( clear )
+ begin
+ Mout_n = RESET_SEED;
+ end
+ else if( enable )
+ begin
+ Mout_n[0] = data_in[0] ^ data_in[4] ^ data_in[8] ^ data_in[11] ^ data_in[12] ^ Mout_p[0] ^ Mout_p[4] ^ Mout_p[8] ^ Mout_p[11] ^ Mout_p[12] ;
+ Mout_n[1] = data_in[1] ^ data_in[5] ^ data_in[9] ^ data_in[12] ^ data_in[13] ^ Mout_p[1] ^ Mout_p[5] ^ Mout_p[9] ^ Mout_p[12] ^ Mout_p[13] ;
+ Mout_n[2] = data_in[2] ^ data_in[6] ^ data_in[10] ^ data_in[13] ^ data_in[14] ^ Mout_p[2] ^ Mout_p[6] ^ Mout_p[10] ^ Mout_p[13] ^ Mout_p[14] ;
+ Mout_n[3] = data_in[3] ^ data_in[7] ^ data_in[11] ^ data_in[14] ^ data_in[15] ^ Mout_p[3] ^ Mout_p[7] ^ Mout_p[11] ^ Mout_p[14] ^ Mout_p[15] ;
+ Mout_n[4] = data_in[4] ^ data_in[8] ^ data_in[12] ^ data_in[15] ^ Mout_p[4] ^ Mout_p[8] ^ Mout_p[12] ^ Mout_p[15] ;
+ Mout_n[5] = data_in[0] ^ data_in[4] ^ data_in[5] ^ data_in[8] ^ data_in[9] ^ data_in[11] ^ data_in[12] ^ data_in[13] ^ Mout_p[0] ^ Mout_p[4] ^ Mout_p[5] ^ Mout_p[8] ^ Mout_p[9] ^ Mout_p[11] ^ Mout_p[12] ^ Mout_p[13] ;
+ Mout_n[6] = data_in[1] ^ data_in[5] ^ data_in[6] ^ data_in[9] ^ data_in[10] ^ data_in[12] ^ data_in[13] ^ data_in[14] ^ Mout_p[1] ^ Mout_p[5] ^ Mout_p[6] ^ Mout_p[9] ^ Mout_p[10] ^ Mout_p[12] ^ Mout_p[13] ^ Mout_p[14] ;
+ Mout_n[7] = data_in[2] ^ data_in[6] ^ data_in[7] ^ data_in[10] ^ data_in[11] ^ data_in[13] ^ data_in[14] ^ data_in[15] ^ Mout_p[2] ^ Mout_p[6] ^ Mout_p[7] ^ Mout_p[10] ^ Mout_p[11] ^ Mout_p[13] ^ Mout_p[14] ^ Mout_p[15] ;
+ Mout_n[8] = data_in[3] ^ data_in[7] ^ data_in[8] ^ data_in[11] ^ data_in[12] ^ data_in[14] ^ data_in[15] ^ Mout_p[3] ^ Mout_p[7] ^ Mout_p[8] ^ Mout_p[11] ^ Mout_p[12] ^ Mout_p[14] ^ Mout_p[15] ;
+ Mout_n[9] = data_in[4] ^ data_in[8] ^ data_in[9] ^ data_in[12] ^ data_in[13] ^ data_in[15] ^ Mout_p[4] ^ Mout_p[8] ^ Mout_p[9] ^ Mout_p[12] ^ Mout_p[13] ^ Mout_p[15] ;
+ Mout_n[10] = data_in[5] ^ data_in[9] ^ data_in[10] ^ data_in[13] ^ data_in[14] ^ Mout_p[5] ^ Mout_p[9] ^ Mout_p[10] ^ Mout_p[13] ^ Mout_p[14] ;
+ Mout_n[11] = data_in[6] ^ data_in[10] ^ data_in[11] ^ data_in[14] ^ data_in[15] ^ Mout_p[6] ^ Mout_p[10] ^ Mout_p[11] ^ Mout_p[14] ^ Mout_p[15] ;
+ Mout_n[12] = data_in[0] ^ data_in[4] ^ data_in[7] ^ data_in[8] ^ data_in[15] ^ Mout_p[0] ^ Mout_p[4] ^ Mout_p[7] ^ Mout_p[8] ^ Mout_p[15] ;
+ Mout_n[13] = data_in[1] ^ data_in[5] ^ data_in[8] ^ data_in[9] ^ Mout_p[1] ^ Mout_p[5] ^ Mout_p[8] ^ Mout_p[9] ;
+ Mout_n[14] = data_in[2] ^ data_in[6] ^ data_in[9] ^ data_in[10] ^ Mout_p[2] ^ Mout_p[6] ^ Mout_p[9] ^ Mout_p[10] ;
+ Mout_n[15] = data_in[3] ^ data_in[7] ^ data_in[10] ^ data_in[11] ^ Mout_p[3] ^ Mout_p[7] ^ Mout_p[10] ^ Mout_p[11] ;
+ end
+ end
+
+ assign CRC = Mout_p;
+endmodule
diff --git a/common/hsdaoh/hsdaoh_core.v b/common/hsdaoh/hsdaoh_core.v
index f238e81..f8aaa9b 100644
--- a/common/hsdaoh/hsdaoh_core.v
+++ b/common/hsdaoh/hsdaoh_core.v
@@ -18,6 +18,21 @@ module hsdaoh_core
input wire [15:0] data_in
);
+ parameter USE_CRC = 1;
+
+ reg crc_enable = 1'b0;
+ wire [15:0] crc_out;
+ reg [15:0] last_line_crc;
+
+ crc16_ccitt crc16_ccitt (
+ .CLK(clk_pixel),
+ .RSTn(rstn),
+ .data_in({hdmi_data[15:8], hdmi_data[23:16]}),
+ .enable(crc_enable),
+ .clear(!crc_enable),
+ .CRC(crc_out)
+ );
+
localparam [31:0] MAGIC = 32'hda7acab1;
reg [23:0] hdmi_data = 24'h000000;
@@ -35,40 +50,56 @@ module hsdaoh_core
always @(posedge clk_pixel) begin
- if ((cx == screen_width-1) && (cy < screen_height)) begin
- // last word of line contains counter of words per line
- hdmi_data <= {status_nibble[3:0], line_word_cnt[11:0], 8'h00};
+ if (cy < screen_height) begin
+ if (USE_CRC && (cx == 0))
+ crc_enable <= 1'b1;
- end else if ((cx < screen_width) && (cy < screen_height)) begin
- if (fifo_read_en && !fifo_empty) begin
- // regular output of FIFO data
- hdmi_data <= {data_in[15:0], 8'h00};
-
- // increment line payload counter
- line_word_cnt <= line_word_cnt + 1'b1;
- end else begin
- // output idle counter
- hdmi_data <= {idle_counter[15:8], idle_counter[7:0], 8'h00};
-
- // increment idle counter
- idle_counter <= idle_counter + 1'b1;
+ if (USE_CRC && (cx == screen_width+1)) begin
+ last_line_crc <= crc_out;
+ crc_enable <= 1'b0;
end
- end else
- line_word_cnt <= 16'h0000;
- // Enable reading before beginning of next line
- if ((cx == frame_width-1) && (cy < screen_height-1) && !fifo_empty)
- fifo_read_en = 1'b1;
+ if (cx == screen_width-1) begin
+ // last word of line contains counter of words per line
+ hdmi_data <= {status_nibble[3:0], line_word_cnt[11:0], 8'h00};
+
+ end else if (USE_CRC && (cx == screen_width-2)) begin
+ // second last word contains CRC
+ hdmi_data <= {last_line_crc, 8'h00};
+
+ end else if (cx < screen_width) begin
+ if (fifo_read_en && !fifo_empty) begin
+ // regular output of FIFO data
+ hdmi_data <= {data_in[15:0], 8'h00};
+
+ // increment line payload counter
+ line_word_cnt <= line_word_cnt + 1'b1;
+ end else begin
+ // output idle counter
+ hdmi_data <= {idle_counter[15:8], idle_counter[7:0], 8'h00};
+
+ // increment idle counter
+ idle_counter <= idle_counter + 1'b1;
+ end
+ end else
+ line_word_cnt <= 16'h0000;
+
+ // 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;
+ 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;
+ end
// switch read off during blanking
if (cy > screen_height)
fifo_read_en = 1'b0;
- // 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) && (cy < screen_height))
- fifo_read_en = 1'b0;
-
// switch read off when FIFO has only one word remaining
if (fifo_aempty)
fifo_read_en = 1'b0;
@@ -97,10 +128,10 @@ always @(posedge clk_pixel) begin
9 : status_nibble <= frame_cnt[7:4];
10 : status_nibble <= frame_cnt[11:8];
11 : status_nibble <= frame_cnt[15:12];
+ 14 : status_nibble <= { 3'b000, USE_CRC };
default : status_nibble <= 4'h0;
endcase
end
-
end
wire tmds_clock;
diff --git a/hsdaoh_nano20k_test.gprj b/hsdaoh_nano20k_test.gprj
index 32a8ed6..977b889 100644
--- a/hsdaoh_nano20k_test.gprj
+++ b/hsdaoh_nano20k_test.gprj
@@ -17,6 +17,7 @@
+
diff --git a/hsdaoh_nano4k_test.gprj b/hsdaoh_nano4k_test.gprj
index dcb9f28..8573657 100644
--- a/hsdaoh_nano4k_test.gprj
+++ b/hsdaoh_nano4k_test.gprj
@@ -17,6 +17,7 @@
+
diff --git a/hsdaoh_nano9k_test.gprj b/hsdaoh_nano9k_test.gprj
index 938867a..7f3ab63 100644
--- a/hsdaoh_nano9k_test.gprj
+++ b/hsdaoh_nano9k_test.gprj
@@ -17,6 +17,7 @@
+
diff --git a/hsdaoh_primer20k_test.gprj b/hsdaoh_primer20k_test.gprj
index e7caa7f..86fc64a 100644
--- a/hsdaoh_primer20k_test.gprj
+++ b/hsdaoh_primer20k_test.gprj
@@ -17,6 +17,7 @@
+
diff --git a/hsdaoh_primer25k_test.gprj b/hsdaoh_primer25k_test.gprj
index f37ca6a..ee08865 100644
--- a/hsdaoh_primer25k_test.gprj
+++ b/hsdaoh_primer25k_test.gprj
@@ -17,6 +17,7 @@
+