From 1b2d0115816669986399ac8a9637dadb1873c74d Mon Sep 17 00:00:00 2001 From: Steve Markgraf Date: Fri, 6 Dec 2024 00:21:09 +0100 Subject: [PATCH] hsdaoh_core: add CRC16 checksum support Output is identical to the implementation in hsdaoh-rp2350. --- common/hsdaoh/crc16_ccitt.v | 86 +++++++++++++++++++++++++++++++++++++ common/hsdaoh/hsdaoh_core.v | 85 ++++++++++++++++++++++++------------ hsdaoh_nano20k_test.gprj | 1 + hsdaoh_nano4k_test.gprj | 1 + hsdaoh_nano9k_test.gprj | 1 + hsdaoh_primer20k_test.gprj | 1 + hsdaoh_primer25k_test.gprj | 1 + 7 files changed, 149 insertions(+), 27 deletions(-) create mode 100644 common/hsdaoh/crc16_ccitt.v 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 @@ +