From 0f81876e54f6ebfe7f1fbba21b3c34bb65a1f76a Mon Sep 17 00:00:00 2001 From: Sameer Puri Date: Fri, 2 Apr 2021 10:54:22 -0400 Subject: [PATCH] use sv2v to convert to verilog for #24 --- src/Manifest.py | 20 +- src/audio_clock_regeneration_packet.sv | 73 ---- src/audio_clock_regeneration_packet.v | 72 ++++ src/audio_info_frame.sv | 53 --- src/audio_info_frame.v | 35 ++ src/audio_sample_packet.sv | 109 ------ src/audio_sample_packet.v | 63 ++++ src/auxiliary_video_information_info_frame.sv | 80 ----- src/auxiliary_video_information_info_frame.v | 66 ++++ src/hdmi.sv | 336 ------------------ src/hdmi.v | 296 +++++++++++++++ src/packet_assembler.sv | 86 ----- src/packet_assembler.v | 65 ++++ src/packet_picker.sv | 193 ---------- src/packet_picker.v | 206 +++++++++++ src/serializer.sv | 247 ------------- src/serializer.v | 63 ++++ src/source_product_description_info_frame.sv | 64 ---- src/source_product_description_info_frame.v | 44 +++ src/tmds_channel.sv | 169 --------- src/tmds_channel.v | 125 +++++++ top/top.sv | 4 +- 22 files changed, 1047 insertions(+), 1422 deletions(-) delete mode 100644 src/audio_clock_regeneration_packet.sv create mode 100644 src/audio_clock_regeneration_packet.v delete mode 100644 src/audio_info_frame.sv create mode 100644 src/audio_info_frame.v delete mode 100644 src/audio_sample_packet.sv create mode 100644 src/audio_sample_packet.v delete mode 100644 src/auxiliary_video_information_info_frame.sv create mode 100644 src/auxiliary_video_information_info_frame.v delete mode 100644 src/hdmi.sv create mode 100644 src/hdmi.v delete mode 100644 src/packet_assembler.sv create mode 100644 src/packet_assembler.v delete mode 100644 src/packet_picker.sv create mode 100644 src/packet_picker.v delete mode 100644 src/serializer.sv create mode 100644 src/serializer.v delete mode 100644 src/source_product_description_info_frame.sv create mode 100644 src/source_product_description_info_frame.v delete mode 100644 src/tmds_channel.sv create mode 100644 src/tmds_channel.v diff --git a/src/Manifest.py b/src/Manifest.py index a6a7af3..19e96cf 100644 --- a/src/Manifest.py +++ b/src/Manifest.py @@ -1,12 +1,12 @@ files = [ - "hdmi.sv", - "tmds_channel.sv", - "packet_assembler.sv", - "packet_picker.sv", - "serializer.sv", - "auxiliary_video_information_info_frame.sv", - "source_product_description_info_frame.sv", - "audio_clock_regeneration_packet.sv", - "audio_info_frame.sv", - "audio_sample_packet.sv" + "hdmi.v", + "tmds_channel.v", + "packet_assembler.v", + "packet_picker.v", + "serializer.v", + "auxiliary_video_information_info_frame.v", + "source_product_description_info_frame.v", + "audio_clock_regeneration_packet.v", + "audio_info_frame.v", + "audio_sample_packet.v" ] diff --git a/src/audio_clock_regeneration_packet.sv b/src/audio_clock_regeneration_packet.sv deleted file mode 100644 index f1b2921..0000000 --- a/src/audio_clock_regeneration_packet.sv +++ /dev/null @@ -1,73 +0,0 @@ -// Implementation of HDMI audio clock regeneration packet -// By Sameer Puri https://github.com/sameer - -// See HDMI 1.4b Section 5.3.3 -module audio_clock_regeneration_packet -#( - parameter real VIDEO_RATE = 25.2E6, - parameter int AUDIO_RATE = 48e3 -) -( - input logic clk_pixel, - input logic clk_audio, - output logic clk_audio_counter_wrap = 0, - output logic [23:0] header, - output logic [55:0] sub [3:0] -); - -// See Section 7.2.3, values derived from "Other" row in Tables 7-1, 7-2, 7-3. -localparam bit [19:0] N = AUDIO_RATE % 125 == 0 ? 20'(16 * AUDIO_RATE / 125) : AUDIO_RATE % 225 == 0 ? 20'(196 * AUDIO_RATE / 225) : 20'(AUDIO_RATE * 16 / 125); - -localparam int CLK_AUDIO_COUNTER_WIDTH = $clog2(N / 128); -localparam bit [CLK_AUDIO_COUNTER_WIDTH-1:0] CLK_AUDIO_COUNTER_END = CLK_AUDIO_COUNTER_WIDTH'(N / 128 - 1); -logic [CLK_AUDIO_COUNTER_WIDTH-1:0] clk_audio_counter = CLK_AUDIO_COUNTER_WIDTH'(0); -logic internal_clk_audio_counter_wrap = 1'd0; -always_ff @(posedge clk_audio) -begin - if (clk_audio_counter == CLK_AUDIO_COUNTER_END) - begin - clk_audio_counter <= CLK_AUDIO_COUNTER_WIDTH'(0); - internal_clk_audio_counter_wrap <= !internal_clk_audio_counter_wrap; - end - else - clk_audio_counter <= clk_audio_counter + 1'd1; -end - -logic [1:0] clk_audio_counter_wrap_synchronizer_chain = 2'd0; -always_ff @(posedge clk_pixel) - clk_audio_counter_wrap_synchronizer_chain <= {internal_clk_audio_counter_wrap, clk_audio_counter_wrap_synchronizer_chain[1]}; - -localparam bit [19:0] CYCLE_TIME_STAMP_COUNTER_IDEAL = 20'(int'(VIDEO_RATE * int'(N) / 128 / AUDIO_RATE)); -localparam int CYCLE_TIME_STAMP_COUNTER_WIDTH = $clog2(20'(int'(real'(CYCLE_TIME_STAMP_COUNTER_IDEAL) * 1.1))); // Account for 10% deviation in audio clock - -logic [19:0] cycle_time_stamp = 20'd0; -logic [CYCLE_TIME_STAMP_COUNTER_WIDTH-1:0] cycle_time_stamp_counter = CYCLE_TIME_STAMP_COUNTER_WIDTH'(0); -always_ff @(posedge clk_pixel) -begin - if (clk_audio_counter_wrap_synchronizer_chain[1] ^ clk_audio_counter_wrap_synchronizer_chain[0]) - begin - cycle_time_stamp_counter <= CYCLE_TIME_STAMP_COUNTER_WIDTH'(0); - cycle_time_stamp <= {(20-CYCLE_TIME_STAMP_COUNTER_WIDTH)'(0), cycle_time_stamp_counter + CYCLE_TIME_STAMP_COUNTER_WIDTH'(1)}; - clk_audio_counter_wrap <= !clk_audio_counter_wrap; - end - else - cycle_time_stamp_counter <= cycle_time_stamp_counter + CYCLE_TIME_STAMP_COUNTER_WIDTH'(1); -end - -// "An HDMI Sink shall ignore bytes HB1 and HB2 of the Audio Clock Regeneration Packet header." -`ifdef MODEL_TECH -assign header = {8'd0, 8'd0, 8'd1}; -`else -assign header = {8'dX, 8'dX, 8'd1}; -`endif - -// "The four Subpackets each contain the same Audio Clock regeneration Subpacket." -genvar i; -generate - for (i = 0; i < 4; i++) - begin: same_packet - assign sub[i] = {N[7:0], N[15:8], {4'd0, N[19:16]}, cycle_time_stamp[7:0], cycle_time_stamp[15:8], {4'd0, cycle_time_stamp[19:16]}, 8'd0}; - end -endgenerate - -endmodule diff --git a/src/audio_clock_regeneration_packet.v b/src/audio_clock_regeneration_packet.v new file mode 100644 index 0000000..c637e4a --- /dev/null +++ b/src/audio_clock_regeneration_packet.v @@ -0,0 +1,72 @@ +module audio_clock_regeneration_packet ( + clk_pixel, + clk_audio, + clk_audio_counter_wrap, + header, + sub +); + parameter real VIDEO_RATE = 25.2E6; + parameter signed [31:0] AUDIO_RATE = 48e3; + input wire clk_pixel; + input wire clk_audio; + output reg clk_audio_counter_wrap = 0; + output wire [23:0] header; + output wire [223:0] sub; + function automatic signed [19:0] sv2v_cast_20_signed; + input reg signed [19:0] inp; + sv2v_cast_20_signed = inp; + endfunction + localparam [19:0] N = ((AUDIO_RATE % 125) == 0 ? sv2v_cast_20_signed((16 * AUDIO_RATE) / 125) : ((AUDIO_RATE % 225) == 0 ? sv2v_cast_20_signed((196 * AUDIO_RATE) / 225) : sv2v_cast_20_signed((AUDIO_RATE * 16) / 125))); + localparam signed [31:0] CLK_AUDIO_COUNTER_WIDTH = $clog2(N / 128); + function automatic [CLK_AUDIO_COUNTER_WIDTH - 1:0] sv2v_cast_C4DBD; + input reg [CLK_AUDIO_COUNTER_WIDTH - 1:0] inp; + sv2v_cast_C4DBD = inp; + endfunction + localparam [CLK_AUDIO_COUNTER_WIDTH - 1:0] CLK_AUDIO_COUNTER_END = sv2v_cast_C4DBD((N / 128) - 1); + function automatic signed [CLK_AUDIO_COUNTER_WIDTH - 1:0] sv2v_cast_C4DBD_signed; + input reg signed [CLK_AUDIO_COUNTER_WIDTH - 1:0] inp; + sv2v_cast_C4DBD_signed = inp; + endfunction + reg [CLK_AUDIO_COUNTER_WIDTH - 1:0] clk_audio_counter = sv2v_cast_C4DBD_signed(0); + reg internal_clk_audio_counter_wrap = 1'd0; + always @(posedge clk_audio) + if (clk_audio_counter == CLK_AUDIO_COUNTER_END) begin + clk_audio_counter <= sv2v_cast_C4DBD_signed(0); + internal_clk_audio_counter_wrap <= !internal_clk_audio_counter_wrap; + end + else + clk_audio_counter <= clk_audio_counter + 1'd1; + reg [1:0] clk_audio_counter_wrap_synchronizer_chain = 2'd0; + always @(posedge clk_pixel) clk_audio_counter_wrap_synchronizer_chain <= {internal_clk_audio_counter_wrap, clk_audio_counter_wrap_synchronizer_chain[1]}; + function automatic signed [31:0] sv2v_cast_32_signed; + input reg signed [31:0] inp; + sv2v_cast_32_signed = inp; + endfunction + localparam [19:0] CYCLE_TIME_STAMP_COUNTER_IDEAL = sv2v_cast_20_signed(sv2v_cast_32_signed(((VIDEO_RATE * sv2v_cast_32_signed(N)) / 128) / AUDIO_RATE)); + localparam signed [31:0] CYCLE_TIME_STAMP_COUNTER_WIDTH = $clog2(sv2v_cast_20_signed(sv2v_cast_32_signed(CYCLE_TIME_STAMP_COUNTER_IDEAL * 1.1))); + reg [19:0] cycle_time_stamp = 20'd0; + function automatic signed [CYCLE_TIME_STAMP_COUNTER_WIDTH - 1:0] sv2v_cast_EFA8E_signed; + input reg signed [CYCLE_TIME_STAMP_COUNTER_WIDTH - 1:0] inp; + sv2v_cast_EFA8E_signed = inp; + endfunction + reg [CYCLE_TIME_STAMP_COUNTER_WIDTH - 1:0] cycle_time_stamp_counter = sv2v_cast_EFA8E_signed(0); + function automatic signed [(20 - CYCLE_TIME_STAMP_COUNTER_WIDTH) - 1:0] sv2v_cast_66F15_signed; + input reg signed [(20 - CYCLE_TIME_STAMP_COUNTER_WIDTH) - 1:0] inp; + sv2v_cast_66F15_signed = inp; + endfunction + always @(posedge clk_pixel) + if (clk_audio_counter_wrap_synchronizer_chain[1] ^ clk_audio_counter_wrap_synchronizer_chain[0]) begin + cycle_time_stamp_counter <= sv2v_cast_EFA8E_signed(0); + cycle_time_stamp <= {sv2v_cast_66F15_signed(0), cycle_time_stamp_counter + sv2v_cast_EFA8E_signed(1)}; + clk_audio_counter_wrap <= !clk_audio_counter_wrap; + end + else + cycle_time_stamp_counter <= cycle_time_stamp_counter + sv2v_cast_EFA8E_signed(1); + assign header = 24'bxxxxxxxxxxxxxxxx00000001; + genvar i; + generate + for (i = 0; i < 4; i = i + 1) begin : same_packet + assign sub[i * 56+:56] = {N[7:0], N[15:8], {4'd0, N[19:16]}, cycle_time_stamp[7:0], cycle_time_stamp[15:8], {4'd0, cycle_time_stamp[19:16]}, 8'd0}; + end + endgenerate +endmodule diff --git a/src/audio_info_frame.sv b/src/audio_info_frame.sv deleted file mode 100644 index 81b503b..0000000 --- a/src/audio_info_frame.sv +++ /dev/null @@ -1,53 +0,0 @@ -// Implementation of HDMI audio info frame -// By Sameer Puri https://github.com/sameer - -// See Section 8.2.2 -module audio_info_frame -#( - parameter bit [2:0] AUDIO_CHANNEL_COUNT = 3'd1, // 2 channels. See CEA-861-D table 17 for details. - parameter bit [7:0] CHANNEL_ALLOCATION = 8'h00, // Channel 0 = Front Left, Channel 1 = Front Right (0-indexed) - parameter bit DOWN_MIX_INHIBITED = 1'b0, // Permitted or no information about any assertion of this. The DM_INH field is to be set only for DVD-Audio applications. - parameter bit [3:0] LEVEL_SHIFT_VALUE = 4'd0, // 4-bit unsigned number from 0dB up to 15dB, used for downmixing. - parameter bit [1:0] LOW_FREQUENCY_EFFECTS_PLAYBACK_LEVEL = 2'b00 // No information, LFE = bass-only info < 120Hz, used in Dolby Surround. -) -( - output logic [23:0] header, - output logic [55:0] sub [3:0] -); - -// NOTE—HDMI requires the coding type, sample size and sample frequency fields to be set to 0 ("Refer to Stream Header") as these items are carried in the audio stream -localparam bit [3:0] AUDIO_CODING_TYPE = 4'd0; // Refer to stream header. -localparam bit [2:0] SAMPLING_FREQUENCY = 3'd0; // Refer to stream header. -localparam bit [1:0] SAMPLE_SIZE = 2'd0; // Refer to stream header. - -localparam bit [4:0] LENGTH = 5'd10; -localparam bit [7:0] VERSION = 8'd1; -localparam bit [6:0] TYPE = 7'd4; - -assign header = {{3'b0, LENGTH}, VERSION, {1'b1, TYPE}}; - -// PB0-PB6 = sub0 -// PB7-13 = sub1 -// PB14-20 = sub2 -// PB21-27 = sub3 -logic [7:0] packet_bytes [27:0]; - -assign packet_bytes[0] = 8'd1 + ~(header[23:16] + header[15:8] + header[7:0] + packet_bytes[5] + packet_bytes[4] + packet_bytes[3] + packet_bytes[2] + packet_bytes[1]); -assign packet_bytes[1] = {AUDIO_CODING_TYPE, 1'b0, AUDIO_CHANNEL_COUNT}; -assign packet_bytes[2] = {3'd0, SAMPLING_FREQUENCY, SAMPLE_SIZE}; -assign packet_bytes[3] = 8'd0; -assign packet_bytes[4] = CHANNEL_ALLOCATION; -assign packet_bytes[5] = {DOWN_MIX_INHIBITED, LEVEL_SHIFT_VALUE, 1'b0, LOW_FREQUENCY_EFFECTS_PLAYBACK_LEVEL}; - -genvar i; -generate - for (i = 6; i < 28; i++) - begin: pb_reserved - assign packet_bytes[i] = 8'd0; - end - for (i = 0; i < 4; i++) - begin: pb_to_sub - assign sub[i] = {packet_bytes[6 + i*7], packet_bytes[5 + i*7], packet_bytes[4 + i*7], packet_bytes[3 + i*7], packet_bytes[2 + i*7], packet_bytes[1 + i*7], packet_bytes[0 + i*7]}; - end -endgenerate -endmodule diff --git a/src/audio_info_frame.v b/src/audio_info_frame.v new file mode 100644 index 0000000..ef7af89 --- /dev/null +++ b/src/audio_info_frame.v @@ -0,0 +1,35 @@ +module audio_info_frame ( + header, + sub +); + parameter [2:0] AUDIO_CHANNEL_COUNT = 3'd1; + parameter [7:0] CHANNEL_ALLOCATION = 8'h00; + parameter [0:0] DOWN_MIX_INHIBITED = 1'b0; + parameter [3:0] LEVEL_SHIFT_VALUE = 4'd0; + parameter [1:0] LOW_FREQUENCY_EFFECTS_PLAYBACK_LEVEL = 2'b00; + output wire [23:0] header; + output wire [223:0] sub; + localparam [3:0] AUDIO_CODING_TYPE = 4'd0; + localparam [2:0] SAMPLING_FREQUENCY = 3'd0; + localparam [1:0] SAMPLE_SIZE = 2'd0; + localparam [4:0] LENGTH = 5'd10; + localparam [7:0] VERSION = 8'd1; + localparam [6:0] TYPE = 7'd4; + assign header = {{3'b000, LENGTH}, VERSION, {1'b1, TYPE}}; + wire [7:0] packet_bytes [27:0]; + assign packet_bytes[0] = 8'd1 + ~(((((((header[23:16] + header[15:8]) + header[7:0]) + packet_bytes[5]) + packet_bytes[4]) + packet_bytes[3]) + packet_bytes[2]) + packet_bytes[1]); + assign packet_bytes[1] = {AUDIO_CODING_TYPE, 1'b0, AUDIO_CHANNEL_COUNT}; + assign packet_bytes[2] = {3'd0, SAMPLING_FREQUENCY, SAMPLE_SIZE}; + assign packet_bytes[3] = 8'd0; + assign packet_bytes[4] = CHANNEL_ALLOCATION; + assign packet_bytes[5] = {DOWN_MIX_INHIBITED, LEVEL_SHIFT_VALUE, 1'b0, LOW_FREQUENCY_EFFECTS_PLAYBACK_LEVEL}; + genvar i; + generate + for (i = 6; i < 28; i = i + 1) begin : pb_reserved + assign packet_bytes[i] = 8'd0; + end + for (i = 0; i < 4; i = i + 1) begin : pb_to_sub + assign sub[i * 56+:56] = {packet_bytes[6 + (i * 7)], packet_bytes[5 + (i * 7)], packet_bytes[4 + (i * 7)], packet_bytes[3 + (i * 7)], packet_bytes[2 + (i * 7)], packet_bytes[1 + (i * 7)], packet_bytes[i * 7]}; + end + endgenerate +endmodule diff --git a/src/audio_sample_packet.sv b/src/audio_sample_packet.sv deleted file mode 100644 index 7a6f276..0000000 --- a/src/audio_sample_packet.sv +++ /dev/null @@ -1,109 +0,0 @@ -// Implementation of HDMI audio sample packet -// By Sameer Puri https://github.com/sameer - -// Unless otherwise specified, all "See X" references will refer to the HDMI v1.4a specification. - -// See Section 5.3.4 -// 2-channel L-PCM or IEC 61937 audio in IEC 60958 frames with consumer grade IEC 60958-3. -module audio_sample_packet -#( - // A thorough explanation of the below parameters can be found in IEC 60958-3 5.2, 5.3. - - // 0 = Consumer, 1 = Professional - parameter bit GRADE = 1'b0, - - // 0 = LPCM, 1 = IEC 61937 compressed - parameter bit SAMPLE_WORD_TYPE = 1'b0, - - // 0 = asserted, 1 = not asserted - parameter bit COPYRIGHT_NOT_ASSERTED = 1'b1, - - // 000 = no pre-emphasis, 001 = 50μs/15μs pre-emphasis - parameter bit [2:0] PRE_EMPHASIS = 3'b000, - - // Only one valid value - parameter bit [1:0] MODE = 2'b00, - - // Set to all 0s for general device. - parameter bit [7:0] CATEGORY_CODE = 8'd0, - - // TODO: not really sure what this is... - // 0 = "Do no take into account" - parameter bit [3:0] SOURCE_NUMBER = 4'd0, - - // 0000 = 44.1 kHz - parameter bit [3:0] SAMPLING_FREQUENCY = 4'b0000, - - // Normal accuracy: +/- 1000 * 10E-6 (00), High accuracy +/- 50 * 10E-6 (01) - parameter bit [1:0] CLOCK_ACCURACY = 2'b00, - - // 3-bit representation of the number of bits to subtract (except 101 is actually subtract 0) with LSB first, followed by maxmium length of 20 bits (0) or 24 bits (1) - parameter bit [3:0] WORD_LENGTH = 0, - - // Frequency prior to conversion in a consumer playback system. 0000 = not indicated. - parameter bit [3:0] ORIGINAL_SAMPLING_FREQUENCY = 4'b0000, - - // 2-channel = 0, >= 3-channel = 1 - parameter bit LAYOUT = 1'b0 - -) -( - input logic [7:0] frame_counter, - // See IEC 60958-1 4.4 and Annex A. 0 indicates the signal is suitable for decoding to an analog audio signal. - input logic [1:0] valid_bit [3:0], - // See IEC 60958-3 Section 6. 0 indicates that no user data is being sent - input logic [1:0] user_data_bit [3:0], - input logic [23:0] audio_sample_word [3:0] [1:0], - input logic [3:0] audio_sample_word_present, - output logic [23:0] header, - output logic [55:0] sub [3:0] -); - -// Left/right channel for stereo audio -logic [3:0] CHANNEL_LEFT = 4'd1; -logic [3:0] CHANNEL_RIGHT = 4'd2; - -localparam bit [7:0] CHANNEL_STATUS_LENGTH = 8'd192; -// See IEC 60958-1 5.1, Table 2 -logic [192-1:0] channel_status_left; -assign channel_status_left = {152'd0, ORIGINAL_SAMPLING_FREQUENCY, WORD_LENGTH, 2'b00, CLOCK_ACCURACY, SAMPLING_FREQUENCY, CHANNEL_LEFT, SOURCE_NUMBER, CATEGORY_CODE, MODE, PRE_EMPHASIS, COPYRIGHT_NOT_ASSERTED, SAMPLE_WORD_TYPE, GRADE}; -logic [CHANNEL_STATUS_LENGTH-1:0] channel_status_right; -assign channel_status_right = {152'd0, ORIGINAL_SAMPLING_FREQUENCY, WORD_LENGTH, 2'b00, CLOCK_ACCURACY, SAMPLING_FREQUENCY, CHANNEL_RIGHT, SOURCE_NUMBER, CATEGORY_CODE, MODE, PRE_EMPHASIS, COPYRIGHT_NOT_ASSERTED, SAMPLE_WORD_TYPE, GRADE}; - - -// See HDMI 1.4a Table 5-12: Audio Sample Packet Header. -assign header[19:12] = {4'b0000, {3'b000, LAYOUT}}; -assign header[7:0] = 8'd2; -logic [1:0] parity_bit [3:0]; -logic [7:0] aligned_frame_counter [3:0]; -genvar i; -generate - for (i = 0; i < 4; i++) - begin: sample_based_assign - always_comb - begin - if (8'(frame_counter + i) >= CHANNEL_STATUS_LENGTH) - aligned_frame_counter[i] = 8'(frame_counter + i - CHANNEL_STATUS_LENGTH); - else - aligned_frame_counter[i] = 8'(frame_counter + i); - end - assign header[23 - (3-i)] = aligned_frame_counter[i] == 8'd0 && audio_sample_word_present[i]; - assign header[11 - (3-i)] = audio_sample_word_present[i]; - assign parity_bit[i][0] = ^{channel_status_left[aligned_frame_counter[i]], user_data_bit[i][0], valid_bit[i][0], audio_sample_word[i][0]}; - assign parity_bit[i][1] = ^{channel_status_right[aligned_frame_counter[i]], user_data_bit[i][1], valid_bit[i][1], audio_sample_word[i][1]}; - // See HDMI 1.4a Table 5-13: Audio Sample Subpacket. - always_comb - begin - if (audio_sample_word_present[i]) - sub[i] = {{parity_bit[i][1], channel_status_right[aligned_frame_counter[i]], user_data_bit[i][1], valid_bit[i][1], parity_bit[i][0], channel_status_left[aligned_frame_counter[i]], user_data_bit[i][0], valid_bit[i][0]}, audio_sample_word[i][1], audio_sample_word[i][0]}; - else - `ifdef MODEL_TECH - sub[i] = 56'd0; - `else - sub[i] = 56'dx; - `endif - end - end -endgenerate - -endmodule diff --git a/src/audio_sample_packet.v b/src/audio_sample_packet.v new file mode 100644 index 0000000..dd5a4c4 --- /dev/null +++ b/src/audio_sample_packet.v @@ -0,0 +1,63 @@ +module audio_sample_packet ( + frame_counter, + valid_bit, + user_data_bit, + audio_sample_word, + audio_sample_word_present, + header, + sub +); + parameter [0:0] GRADE = 1'b0; + parameter [0:0] SAMPLE_WORD_TYPE = 1'b0; + parameter [0:0] COPYRIGHT_NOT_ASSERTED = 1'b1; + parameter [2:0] PRE_EMPHASIS = 3'b000; + parameter [1:0] MODE = 2'b00; + parameter [7:0] CATEGORY_CODE = 8'd0; + parameter [3:0] SOURCE_NUMBER = 4'd0; + parameter [3:0] SAMPLING_FREQUENCY = 4'b0000; + parameter [1:0] CLOCK_ACCURACY = 2'b00; + parameter [3:0] WORD_LENGTH = 0; + parameter [3:0] ORIGINAL_SAMPLING_FREQUENCY = 4'b0000; + parameter [0:0] LAYOUT = 1'b0; + input wire [7:0] frame_counter; + input wire [7:0] valid_bit; + input wire [7:0] user_data_bit; + input wire [191:0] audio_sample_word; + input wire [3:0] audio_sample_word_present; + output wire [23:0] header; + output reg [223:0] sub; + wire [3:0] CHANNEL_LEFT = 4'd1; + wire [3:0] CHANNEL_RIGHT = 4'd2; + localparam [7:0] CHANNEL_STATUS_LENGTH = 8'd192; + wire [191:0] channel_status_left; + assign channel_status_left = {152'd0, ORIGINAL_SAMPLING_FREQUENCY, WORD_LENGTH, 2'b00, CLOCK_ACCURACY, SAMPLING_FREQUENCY, CHANNEL_LEFT, SOURCE_NUMBER, CATEGORY_CODE, MODE, PRE_EMPHASIS, COPYRIGHT_NOT_ASSERTED, SAMPLE_WORD_TYPE, GRADE}; + wire [CHANNEL_STATUS_LENGTH - 1:0] channel_status_right; + assign channel_status_right = {152'd0, ORIGINAL_SAMPLING_FREQUENCY, WORD_LENGTH, 2'b00, CLOCK_ACCURACY, SAMPLING_FREQUENCY, CHANNEL_RIGHT, SOURCE_NUMBER, CATEGORY_CODE, MODE, PRE_EMPHASIS, COPYRIGHT_NOT_ASSERTED, SAMPLE_WORD_TYPE, GRADE}; + assign header[19:12] = {4'b0000, {3'b000, LAYOUT}}; + assign header[7:0] = 8'd2; + wire [1:0] parity_bit [3:0]; + reg [7:0] aligned_frame_counter [3:0]; + function automatic [7:0] sv2v_cast_8; + input reg [7:0] inp; + sv2v_cast_8 = inp; + endfunction + genvar i; + generate + for (i = 0; i < 4; i = i + 1) begin : sample_based_assign + always @(*) + if (sv2v_cast_8(frame_counter + i) >= CHANNEL_STATUS_LENGTH) + aligned_frame_counter[i] = sv2v_cast_8((frame_counter + i) - CHANNEL_STATUS_LENGTH); + else + aligned_frame_counter[i] = sv2v_cast_8(frame_counter + i); + assign header[20 + i] = (aligned_frame_counter[i] == 8'd0) && audio_sample_word_present[i]; + assign header[8 + i] = audio_sample_word_present[i]; + assign parity_bit[i][0] = ^{channel_status_left[aligned_frame_counter[i]], user_data_bit[i * 2], valid_bit[i * 2], audio_sample_word[(i * 2) * 24+:24]}; + assign parity_bit[i][1] = ^{channel_status_right[aligned_frame_counter[i]], user_data_bit[(i * 2) + 1], valid_bit[(i * 2) + 1], audio_sample_word[((i * 2) + 1) * 24+:24]}; + always @(*) + if (audio_sample_word_present[i]) + sub[i * 56+:56] = {{parity_bit[i][1], channel_status_right[aligned_frame_counter[i]], user_data_bit[(i * 2) + 1], valid_bit[(i * 2) + 1], parity_bit[i][0], channel_status_left[aligned_frame_counter[i]], user_data_bit[i * 2], valid_bit[i * 2]}, audio_sample_word[((i * 2) + 1) * 24+:24], audio_sample_word[(i * 2) * 24+:24]}; + else + sub[i * 56+:56] = 56'bxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx; + end + endgenerate +endmodule diff --git a/src/auxiliary_video_information_info_frame.sv b/src/auxiliary_video_information_info_frame.sv deleted file mode 100644 index 20c15e3..0000000 --- a/src/auxiliary_video_information_info_frame.sv +++ /dev/null @@ -1,80 +0,0 @@ -// Implementation of HDMI Auxiliary Video InfoFrame packet. -// By Sameer Puri https://github.com/sameer - -// See Section 8.2.1 -module auxiliary_video_information_info_frame -#( - parameter bit [1:0] VIDEO_FORMAT = 2'b00, // 00 = RGB, 01 = YCbCr 4:2:2, 10 = YCbCr 4:4:4 - parameter bit ACTIVE_FORMAT_INFO_PRESENT = 1'b0, // Not valid - parameter bit [1:0] BAR_INFO = 2'b00, // Not valid - parameter bit [1:0] SCAN_INFO = 2'b00, // No data - parameter bit [1:0] COLORIMETRY = 2'b00, // No data - parameter bit [1:0] PICTURE_ASPECT_RATIO = 2'b00, // No data, See CEA-CEB16 for more information about Active Format Description processing. - parameter bit [3:0] ACTIVE_FORMAT_ASPECT_RATIO = 4'b1000, // Not valid unless ACTIVE_FORMAT_INFO_PRESENT = 1'b1, then Same as picture aspect ratio - parameter bit IT_CONTENT = 1'b0, // The IT content bit indicates when picture content is composed according to common IT practice (i.e. without regard to Nyquist criterion) and is unsuitable for analog reconstruction or filtering. When the IT content bit is set to 1, downstream processors should pass pixel data unfiltered and without analog reconstruction. - parameter bit [2:0] EXTENDED_COLORIMETRY = 3'b000, // Not valid unless COLORIMETRY = 2'b11. The extended colorimetry bits, EC2, EC1, and EC0, describe optional colorimetry encoding that may be applicable to some implementations and are always present, whether their information is valid or not (see CEA 861-D Section 7.5.5). - parameter bit [1:0] RGB_QUANTIZATION_RANGE = 2'b00, // Default. Displays conforming to CEA-861-D accept both a limited quantization range of 220 levels (16 to 235) anda full range of 256 levels (0 to 255) when receiving video with RGB color space (see CEA 861-D Sections 5.1, Section 5.2, Section 5.3 and Section 5.4). By default, RGB pixel data values should be assumed to have the limited range when receiving a CE video format, and the full range when receiving an IT format. The quantization bits allow the source to override this default and to explicitly indicate the current RGB quantization range. - parameter bit [1:0] NON_UNIFORM_PICTURE_SCALING = 2'b00, // None. The Nonuniform Picture Scaling bits shall be set if the source device scales the picture or has determined that scaling has been performed in a specific direction. - parameter int VIDEO_ID_CODE = 4, // Same as the one from the HDMI module - parameter bit [1:0] YCC_QUANTIZATION_RANGE = 2'b00, // 00 = Limited, 01 = Full - parameter bit [1:0] CONTENT_TYPE = 2'b00, // No data, becomes Graphics if IT_CONTENT = 1'b1. - parameter bit [3:0] PIXEL_REPETITION = 4'b0000 // None -) -( - output logic [23:0] header, - output logic [55:0] sub [3:0] -); - - -localparam bit [4:0] LENGTH = 5'd13; -localparam bit [7:0] VERSION = 8'd2; -localparam bit [6:0] TYPE = 7'd2; - -assign header = {{3'b0, LENGTH}, VERSION, {1'b1, TYPE}}; - -// PB0-PB6 = sub0 -// PB7-13 = sub1 -// PB14-20 = sub2 -// PB21-27 = sub3 -logic [7:0] packet_bytes [27:0]; - -assign packet_bytes[0] = 8'd1 + ~(header[23:16] + header[15:8] + header[7:0] + packet_bytes[13] + packet_bytes[12] + packet_bytes[11] + packet_bytes[10] + packet_bytes[9] + packet_bytes[8] + packet_bytes[7] + packet_bytes[6] + packet_bytes[5] + packet_bytes[4] + packet_bytes[3] + packet_bytes[2] + packet_bytes[1]); -assign packet_bytes[1] = {1'b0, VIDEO_FORMAT, ACTIVE_FORMAT_INFO_PRESENT, BAR_INFO, SCAN_INFO}; -assign packet_bytes[2] = {COLORIMETRY, PICTURE_ASPECT_RATIO, ACTIVE_FORMAT_ASPECT_RATIO}; -assign packet_bytes[3] = {IT_CONTENT, EXTENDED_COLORIMETRY, RGB_QUANTIZATION_RANGE, NON_UNIFORM_PICTURE_SCALING}; -assign packet_bytes[4] = {1'b0, 7'(VIDEO_ID_CODE)}; -assign packet_bytes[5] = {YCC_QUANTIZATION_RANGE, CONTENT_TYPE, PIXEL_REPETITION}; - -genvar i; -generate - if (BAR_INFO != 2'b00) // Assign values to bars if BAR_INFO says they are valid. - begin - assign packet_bytes[6] = 8'hff; - assign packet_bytes[7] = 8'hff; - assign packet_bytes[8] = 8'h00; - assign packet_bytes[9] = 8'h00; - assign packet_bytes[10] = 8'hff; - assign packet_bytes[11] = 8'hff; - assign packet_bytes[12] = 8'h00; - assign packet_bytes[13] = 8'h00; - end else begin - assign packet_bytes[6] = 8'h00; - assign packet_bytes[7] = 8'h00; - assign packet_bytes[8] = 8'h00; - assign packet_bytes[9] = 8'h00; - assign packet_bytes[10] = 8'h00; - assign packet_bytes[11] = 8'h00; - assign packet_bytes[12] = 8'h00; - assign packet_bytes[13] = 8'h00; - end - for (i = 14; i < 28; i++) - begin: pb_reserved - assign packet_bytes[i] = 8'd0; - end - for (i = 0; i < 4; i++) - begin: pb_to_sub - assign sub[i] = {packet_bytes[6 + i*7], packet_bytes[5 + i*7], packet_bytes[4 + i*7], packet_bytes[3 + i*7], packet_bytes[2 + i*7], packet_bytes[1 + i*7], packet_bytes[0 + i*7]}; - end -endgenerate - -endmodule diff --git a/src/auxiliary_video_information_info_frame.v b/src/auxiliary_video_information_info_frame.v new file mode 100644 index 0000000..366fcf0 --- /dev/null +++ b/src/auxiliary_video_information_info_frame.v @@ -0,0 +1,66 @@ +module auxiliary_video_information_info_frame ( + header, + sub +); + parameter [1:0] VIDEO_FORMAT = 2'b00; + parameter [0:0] ACTIVE_FORMAT_INFO_PRESENT = 1'b0; + parameter [1:0] BAR_INFO = 2'b00; + parameter [1:0] SCAN_INFO = 2'b00; + parameter [1:0] COLORIMETRY = 2'b00; + parameter [1:0] PICTURE_ASPECT_RATIO = 2'b00; + parameter [3:0] ACTIVE_FORMAT_ASPECT_RATIO = 4'b1000; + parameter [0:0] IT_CONTENT = 1'b0; + parameter [2:0] EXTENDED_COLORIMETRY = 3'b000; + parameter [1:0] RGB_QUANTIZATION_RANGE = 2'b00; + parameter [1:0] NON_UNIFORM_PICTURE_SCALING = 2'b00; + parameter signed [31:0] VIDEO_ID_CODE = 4; + parameter [1:0] YCC_QUANTIZATION_RANGE = 2'b00; + parameter [1:0] CONTENT_TYPE = 2'b00; + parameter [3:0] PIXEL_REPETITION = 4'b0000; + output wire [23:0] header; + output wire [223:0] sub; + localparam [4:0] LENGTH = 5'd13; + localparam [7:0] VERSION = 8'd2; + localparam [6:0] TYPE = 7'd2; + assign header = {{3'b000, LENGTH}, VERSION, {1'b1, TYPE}}; + wire [7:0] packet_bytes [27:0]; + assign packet_bytes[0] = 8'd1 + ~(((((((((((((((header[23:16] + header[15:8]) + header[7:0]) + packet_bytes[13]) + packet_bytes[12]) + packet_bytes[11]) + packet_bytes[10]) + packet_bytes[9]) + packet_bytes[8]) + packet_bytes[7]) + packet_bytes[6]) + packet_bytes[5]) + packet_bytes[4]) + packet_bytes[3]) + packet_bytes[2]) + packet_bytes[1]); + assign packet_bytes[1] = {1'b0, VIDEO_FORMAT, ACTIVE_FORMAT_INFO_PRESENT, BAR_INFO, SCAN_INFO}; + assign packet_bytes[2] = {COLORIMETRY, PICTURE_ASPECT_RATIO, ACTIVE_FORMAT_ASPECT_RATIO}; + assign packet_bytes[3] = {IT_CONTENT, EXTENDED_COLORIMETRY, RGB_QUANTIZATION_RANGE, NON_UNIFORM_PICTURE_SCALING}; + function automatic signed [6:0] sv2v_cast_7_signed; + input reg signed [6:0] inp; + sv2v_cast_7_signed = inp; + endfunction + assign packet_bytes[4] = {1'b0, sv2v_cast_7_signed(VIDEO_ID_CODE)}; + assign packet_bytes[5] = {YCC_QUANTIZATION_RANGE, CONTENT_TYPE, PIXEL_REPETITION}; + genvar i; + generate + if (BAR_INFO != 2'b00) begin + assign packet_bytes[6] = 8'hff; + assign packet_bytes[7] = 8'hff; + assign packet_bytes[8] = 8'h00; + assign packet_bytes[9] = 8'h00; + assign packet_bytes[10] = 8'hff; + assign packet_bytes[11] = 8'hff; + assign packet_bytes[12] = 8'h00; + assign packet_bytes[13] = 8'h00; + end + else begin + assign packet_bytes[6] = 8'h00; + assign packet_bytes[7] = 8'h00; + assign packet_bytes[8] = 8'h00; + assign packet_bytes[9] = 8'h00; + assign packet_bytes[10] = 8'h00; + assign packet_bytes[11] = 8'h00; + assign packet_bytes[12] = 8'h00; + assign packet_bytes[13] = 8'h00; + end + for (i = 14; i < 28; i = i + 1) begin : pb_reserved + assign packet_bytes[i] = 8'd0; + end + for (i = 0; i < 4; i = i + 1) begin : pb_to_sub + assign sub[i * 56+:56] = {packet_bytes[6 + (i * 7)], packet_bytes[5 + (i * 7)], packet_bytes[4 + (i * 7)], packet_bytes[3 + (i * 7)], packet_bytes[2 + (i * 7)], packet_bytes[1 + (i * 7)], packet_bytes[i * 7]}; + end + endgenerate +endmodule diff --git a/src/hdmi.sv b/src/hdmi.sv deleted file mode 100644 index 94cdd1f..0000000 --- a/src/hdmi.sv +++ /dev/null @@ -1,336 +0,0 @@ -// Implementation of HDMI Spec v1.4a -// By Sameer Puri https://github.com/sameer - -module hdmi -#( - // Defaults to 640x480 which should be supported by almost if not all HDMI sinks. - // See README.md or CEA-861-D for enumeration of video id codes. - // Pixel repetition, interlaced scans and other special output modes are not implemented (yet). - parameter int VIDEO_ID_CODE = 1, - - // Defaults to minimum bit lengths required to represent positions. - // Modify these parameters if you have alternate desired bit lengths. - parameter int BIT_WIDTH = VIDEO_ID_CODE < 4 ? 10 : VIDEO_ID_CODE == 4 ? 11 : 12, - parameter int BIT_HEIGHT = VIDEO_ID_CODE == 16 ? 11: 10, - - // A true HDMI signal sends auxiliary data (i.e. audio, preambles) which prevents it from being parsed by DVI signal sinks. - // HDMI signal sinks are fortunately backwards-compatible with DVI signals. - // Enable this flag if the output should be a DVI signal. You might want to do this to reduce resource usage or if you're only outputting video. - parameter bit DVI_OUTPUT = 1'b0, - - // **All parameters below matter ONLY IF you plan on sending auxiliary data (DVI_OUTPUT == 1'b0)** - - // Specify the refresh rate in Hz you are using for audio calculations - parameter real VIDEO_REFRESH_RATE = 59.94, - - // As specified in Section 7.3, the minimal audio requirements are met: 16-bit or more L-PCM audio at 32 kHz, 44.1 kHz, or 48 kHz. - // See Table 7-4 or README.md for an enumeration of sampling frequencies supported by HDMI. - // Note that sinks may not support rates above 48 kHz. - parameter int AUDIO_RATE = 44100, - - // Defaults to 16-bit audio, the minmimum supported by HDMI sinks. Can be anywhere from 16-bit to 24-bit. - parameter int AUDIO_BIT_WIDTH = 16, - - // Some HDMI sinks will show the source product description below to users (i.e. in a list of inputs instead of HDMI 1, HDMI 2, etc.). - // If you care about this, change it below. - parameter bit [8*8-1:0] VENDOR_NAME = {"Unknown", 8'd0}, // Must be 8 bytes null-padded 7-bit ASCII - parameter bit [8*16-1:0] PRODUCT_DESCRIPTION = {"FPGA", 96'd0}, // Must be 16 bytes null-padded 7-bit ASCII - parameter bit [7:0] SOURCE_DEVICE_INFORMATION = 8'h00 // See README.md or CTA-861-G for the list of valid codes -) -( - input logic clk_pixel_x5, - input logic clk_pixel, - input logic clk_audio, - // synchronous reset back to 0,0 - input logic reset, - input logic [23:0] rgb, - input logic [AUDIO_BIT_WIDTH-1:0] audio_sample_word [1:0], - - // These outputs go to your HDMI port - output logic [2:0] tmds, - output logic tmds_clock, - - // All outputs below this line stay inside the FPGA - // They are used (by you) to pick the color each pixel should have - // i.e. always_ff @(posedge pixel_clk) rgb <= {8'd0, 8'(cx), 8'(cy)}; - output logic [BIT_WIDTH-1:0] cx = BIT_WIDTH'(0), - output logic [BIT_HEIGHT-1:0] cy = BIT_HEIGHT'(0), - - // The screen is at the upper left corner of the frame. - // 0,0 = 0,0 in video - // the frame includes extra space for sending auxiliary data - output logic [BIT_WIDTH-1:0] frame_width, - output logic [BIT_HEIGHT-1:0] frame_height, - output logic [BIT_WIDTH-1:0] screen_width, - output logic [BIT_HEIGHT-1:0] screen_height -); - -localparam int NUM_CHANNELS = 3; -logic hsync; -logic vsync; - -logic [BIT_WIDTH-1:0] hsync_porch_start, hsync_porch_size; -logic [BIT_HEIGHT-1:0] vsync_porch_start, vsync_porch_size; -logic invert; - -// See CEA-861-D for more specifics formats described below. -generate - case (VIDEO_ID_CODE) - 1: - begin - assign frame_width = 800; - assign frame_height = 525; - assign screen_width = 640; - assign screen_height = 480; - assign hsync_porch_start = 16; - assign hsync_porch_size = 96; - assign vsync_porch_start = 10; - assign vsync_porch_size = 2; - assign invert = 1; - end - 2, 3: - begin - assign frame_width = 858; - assign frame_height = 525; - assign screen_width = 720; - assign screen_height = 480; - assign hsync_porch_start = 16; - assign hsync_porch_size = 62; - assign vsync_porch_start = 9; - assign vsync_porch_size = 6; - assign invert = 1; - end - 4: - begin - assign frame_width = 1650; - assign frame_height = 750; - assign screen_width = 1280; - assign screen_height = 720; - assign hsync_porch_start = 110; - assign hsync_porch_size = 40; - assign vsync_porch_start = 5; - assign vsync_porch_size = 5; - assign invert = 0; - end - 16, 34: - begin - assign frame_width = 2200; - assign frame_height = 1125; - assign screen_width = 1920; - assign screen_height = 1080; - assign hsync_porch_start = 88; - assign hsync_porch_size = 44; - assign vsync_porch_start = 4; - assign vsync_porch_size = 5; - assign invert = 0; - end - 17, 18: - begin - assign frame_width = 864; - assign frame_height = 625; - assign screen_width = 720; - assign screen_height = 576; - assign hsync_porch_start = 12; - assign hsync_porch_size = 64; - assign vsync_porch_start = 5; - assign vsync_porch_size = 5; - assign invert = 1; - end - 19: - begin - assign frame_width = 1980; - assign frame_height = 750; - assign screen_width = 1280; - assign screen_height = 720; - assign hsync_porch_start = 440; - assign hsync_porch_size = 40; - assign vsync_porch_start = 5; - assign vsync_porch_size = 5; - assign invert = 0; - end - 95, 105, 97, 107: - begin - assign frame_width = 4400; - assign frame_height = 2250; - assign screen_width = 3840; - assign screen_height = 2160; - assign hsync_porch_start = 176; - assign hsync_porch_size = 88; - assign vsync_porch_start = 8; - assign vsync_porch_size = 10; - assign invert = 0; - end - endcase - assign hsync = invert ^ (cx >= screen_width + hsync_porch_start && cx < screen_width + hsync_porch_start + hsync_porch_size); - assign vsync = invert ^ (cy >= screen_height + vsync_porch_start && cy < screen_height + vsync_porch_start + vsync_porch_size); -endgenerate - -localparam real VIDEO_RATE = (VIDEO_ID_CODE == 1 ? 25.2E6 - : VIDEO_ID_CODE == 2 || VIDEO_ID_CODE == 3 ? 27.027E6 - : VIDEO_ID_CODE == 4 ? 74.25E6 - : VIDEO_ID_CODE == 16 ? 148.5E6 - : VIDEO_ID_CODE == 17 || VIDEO_ID_CODE == 18 ? 27E6 - : VIDEO_ID_CODE == 19 ? 74.25E6 - : VIDEO_ID_CODE == 34 ? 74.25E6 - : VIDEO_ID_CODE == 95 || VIDEO_ID_CODE == 105 || VIDEO_ID_CODE == 97 || VIDEO_ID_CODE == 107 ? 594E6 - : 0) * (VIDEO_REFRESH_RATE == 59.94 || VIDEO_REFRESH_RATE == 29.97 ? 1000.0/1001.0 : 1); // https://groups.google.com/forum/#!topic/sci.engr.advanced-tv/DQcGk5R_zsM - -// Wrap-around pixel position counters indicating the pixel to be generated by the user in THIS clock and sent out in the NEXT clock. -always_ff @(posedge clk_pixel) -begin - if (reset) - begin - cx <= BIT_WIDTH'(0); - cy <= BIT_HEIGHT'(0); - end - else - begin - cx <= cx == frame_width-1'b1 ? BIT_WIDTH'(0) : cx + 1'b1; - cy <= cx == frame_width-1'b1 ? cy == frame_height-1'b1 ? BIT_HEIGHT'(0) : cy + 1'b1 : cy; - end -end - -// See Section 5.2 -logic video_data_period = 0; -always_ff @(posedge clk_pixel) -begin - if (reset) - video_data_period <= 0; - else - video_data_period <= cx < screen_width && cy < screen_height; -end - -logic [2:0] mode = 3'd1; -logic [23:0] video_data = 24'd0; -logic [5:0] control_data = 6'd0; -logic [11:0] data_island_data = 12'd0; - -generate - if (!DVI_OUTPUT) - begin: true_hdmi_output - logic video_guard = 1; - logic video_preamble = 0; - always_ff @(posedge clk_pixel) - begin - if (reset) - begin - video_guard <= 1; - video_preamble <= 0; - end - else - begin - video_guard <= cx >= frame_width - 2 && cx < frame_width && (cy == frame_height - 1 || cy < screen_height); - video_preamble <= cx >= frame_width - 10 && cx < frame_width - 2 && (cy == frame_height - 1 || cy < screen_height); - end - end - - // See Section 5.2.3.1 - int max_num_packets_alongside; - logic [4:0] num_packets_alongside; - always_comb - begin - max_num_packets_alongside = ((frame_width - screen_width) /* VD period */ - 2 /* V guard */ - 8 /* V preamble */ - 12 /* 12px control period */ - 2 /* DI guard */ - 2 /* DI start guard */ - 8 /* DI premable */) / 32; - if (max_num_packets_alongside > 18) - num_packets_alongside = 5'd18; - else - num_packets_alongside = 5'(max_num_packets_alongside); - end - - logic data_island_period_instantaneous; - assign data_island_period_instantaneous = num_packets_alongside > 0 && cx >= screen_width + 10 && cx < screen_width + 10 + num_packets_alongside * 32; - logic packet_enable; - assign packet_enable = data_island_period_instantaneous && 5'(cx + screen_width + 22) == 5'd0; - - logic data_island_guard = 0; - logic data_island_preamble = 0; - logic data_island_period = 0; - always_ff @(posedge clk_pixel) - begin - if (reset) - begin - data_island_guard <= 0; - data_island_preamble <= 0; - data_island_period <= 0; - end - else - begin - data_island_guard <= num_packets_alongside > 0 && ((cx >= screen_width + 8 && cx < screen_width + 10) || (cx >= screen_width + 10 + num_packets_alongside * 32 && cx < screen_width + 10 + num_packets_alongside * 32 + 2)); - data_island_preamble <= num_packets_alongside > 0 && cx >= screen_width && cx < screen_width + 8; - data_island_period <= data_island_period_instantaneous; - end - end - - // See Section 5.2.3.4 - logic [23:0] header; - logic [55:0] sub [3:0]; - logic video_field_end; - assign video_field_end = cx == screen_width - 1'b1 && cy == screen_height - 1'b1; - logic [4:0] packet_pixel_counter; - packet_picker #( - .VIDEO_ID_CODE(VIDEO_ID_CODE), - .VIDEO_RATE(VIDEO_RATE), - .AUDIO_RATE(AUDIO_RATE), - .AUDIO_BIT_WIDTH(AUDIO_BIT_WIDTH), - .VENDOR_NAME(VENDOR_NAME), - .PRODUCT_DESCRIPTION(PRODUCT_DESCRIPTION), - .SOURCE_DEVICE_INFORMATION(SOURCE_DEVICE_INFORMATION) - ) packet_picker (.clk_pixel(clk_pixel), .clk_audio(clk_audio), .reset(reset), .video_field_end(video_field_end), .packet_enable(packet_enable), .packet_pixel_counter(packet_pixel_counter), .audio_sample_word(audio_sample_word), .header(header), .sub(sub)); - logic [8:0] packet_data; - packet_assembler packet_assembler (.clk_pixel(clk_pixel), .reset(reset), .data_island_period(data_island_period), .header(header), .sub(sub), .packet_data(packet_data), .counter(packet_pixel_counter)); - - - always_ff @(posedge clk_pixel) - begin - if (reset) - begin - mode <= 3'd2; - video_data <= 24'd0; - control_data = 6'd0; - data_island_data <= 12'd0; - end - else - begin - mode <= data_island_guard ? 3'd4 : data_island_period ? 3'd3 : video_guard ? 3'd2 : video_data_period ? 3'd1 : 3'd0; - video_data <= rgb; - control_data <= {{1'b0, data_island_preamble}, {1'b0, video_preamble || data_island_preamble}, {vsync, hsync}}; // ctrl3, ctrl2, ctrl1, ctrl0, vsync, hsync - data_island_data[11:4] <= packet_data[8:1]; - data_island_data[3] <= cx != 0; - data_island_data[2] <= packet_data[0]; - data_island_data[1:0] <= {vsync, hsync}; - end - end - end - else // DVI_OUTPUT = 1 - begin - always_ff @(posedge clk_pixel) - begin - if (reset) - begin - mode <= 3'd0; - video_data <= 24'd0; - control_data <= 6'd0; - end - else - begin - mode <= video_data_period ? 3'd1 : 3'd0; - video_data <= rgb; - control_data <= {4'b0000, {vsync, hsync}}; // ctrl3, ctrl2, ctrl1, ctrl0, vsync, hsync - end - end - end -endgenerate - -// All logic below relates to the production and output of the 10-bit TMDS code. -logic [9:0] tmds_internal [NUM_CHANNELS-1:0] /* verilator public_flat */ ; -genvar i; -generate - // TMDS code production. - for (i = 0; i < NUM_CHANNELS; i++) - begin: tmds_gen - tmds_channel #(.CN(i)) tmds_channel (.clk_pixel(clk_pixel), .video_data(video_data[i*8+7:i*8]), .data_island_data(data_island_data[i*4+3:i*4]), .control_data(control_data[i*2+1:i*2]), .mode(mode), .tmds(tmds_internal[i])); - end -endgenerate - -serializer #(.NUM_CHANNELS(NUM_CHANNELS), .VIDEO_RATE(VIDEO_RATE)) serializer(.clk_pixel(clk_pixel), .clk_pixel_x5(clk_pixel_x5), .reset(reset), .tmds_internal(tmds_internal), .tmds(tmds), .tmds_clock(tmds_clock)); - -endmodule diff --git a/src/hdmi.v b/src/hdmi.v new file mode 100644 index 0000000..b50df59 --- /dev/null +++ b/src/hdmi.v @@ -0,0 +1,296 @@ +module hdmi ( + clk_pixel_x5, + clk_pixel, + clk_audio, + reset, + rgb, + audio_sample_word, + tmds, + tmds_clock, + cx, + cy, + frame_width, + frame_height, + screen_width, + screen_height +); + parameter signed [31:0] VIDEO_ID_CODE = 1; + parameter signed [31:0] BIT_WIDTH = (VIDEO_ID_CODE < 4 ? 10 : (VIDEO_ID_CODE == 4 ? 11 : 12)); + parameter signed [31:0] BIT_HEIGHT = (VIDEO_ID_CODE == 16 ? 11 : 10); + parameter [0:0] DVI_OUTPUT = 1'b0; + parameter real VIDEO_REFRESH_RATE = 59.94; + parameter signed [31:0] AUDIO_RATE = 44100; + parameter signed [31:0] AUDIO_BIT_WIDTH = 16; + parameter [63:0] VENDOR_NAME = {"Unknown", 8'd0}; + parameter [127:0] PRODUCT_DESCRIPTION = {"FPGA", 96'd0}; + parameter [7:0] SOURCE_DEVICE_INFORMATION = 8'h00; + input wire clk_pixel_x5; + input wire clk_pixel; + input wire clk_audio; + input wire reset; + input wire [23:0] rgb; + input wire [(2 * AUDIO_BIT_WIDTH) - 1:0] audio_sample_word; + output wire [2:0] tmds; + output wire tmds_clock; + function automatic signed [BIT_WIDTH - 1:0] sv2v_cast_C479B_signed; + input reg signed [BIT_WIDTH - 1:0] inp; + sv2v_cast_C479B_signed = inp; + endfunction + output reg [BIT_WIDTH - 1:0] cx = sv2v_cast_C479B_signed(0); + function automatic signed [BIT_HEIGHT - 1:0] sv2v_cast_4D393_signed; + input reg signed [BIT_HEIGHT - 1:0] inp; + sv2v_cast_4D393_signed = inp; + endfunction + output reg [BIT_HEIGHT - 1:0] cy = sv2v_cast_4D393_signed(0); + output wire [BIT_WIDTH - 1:0] frame_width; + output wire [BIT_HEIGHT - 1:0] frame_height; + output wire [BIT_WIDTH - 1:0] screen_width; + output wire [BIT_HEIGHT - 1:0] screen_height; + localparam signed [31:0] NUM_CHANNELS = 3; + wire hsync; + wire vsync; + wire [BIT_WIDTH - 1:0] hsync_porch_start; + wire [BIT_WIDTH - 1:0] hsync_porch_size; + wire [BIT_HEIGHT - 1:0] vsync_porch_start; + wire [BIT_HEIGHT - 1:0] vsync_porch_size; + wire invert; + generate + case (VIDEO_ID_CODE) + 1: begin + assign frame_width = 800; + assign frame_height = 525; + assign screen_width = 640; + assign screen_height = 480; + assign hsync_porch_start = 16; + assign hsync_porch_size = 96; + assign vsync_porch_start = 10; + assign vsync_porch_size = 2; + assign invert = 1; + end + 2, 3: begin + assign frame_width = 858; + assign frame_height = 525; + assign screen_width = 720; + assign screen_height = 480; + assign hsync_porch_start = 16; + assign hsync_porch_size = 62; + assign vsync_porch_start = 9; + assign vsync_porch_size = 6; + assign invert = 1; + end + 4: begin + assign frame_width = 1650; + assign frame_height = 750; + assign screen_width = 1280; + assign screen_height = 720; + assign hsync_porch_start = 110; + assign hsync_porch_size = 40; + assign vsync_porch_start = 5; + assign vsync_porch_size = 5; + assign invert = 0; + end + 16, 34: begin + assign frame_width = 2200; + assign frame_height = 1125; + assign screen_width = 1920; + assign screen_height = 1080; + assign hsync_porch_start = 88; + assign hsync_porch_size = 44; + assign vsync_porch_start = 4; + assign vsync_porch_size = 5; + assign invert = 0; + end + 17, 18: begin + assign frame_width = 864; + assign frame_height = 625; + assign screen_width = 720; + assign screen_height = 576; + assign hsync_porch_start = 12; + assign hsync_porch_size = 64; + assign vsync_porch_start = 5; + assign vsync_porch_size = 5; + assign invert = 1; + end + 19: begin + assign frame_width = 1980; + assign frame_height = 750; + assign screen_width = 1280; + assign screen_height = 720; + assign hsync_porch_start = 440; + assign hsync_porch_size = 40; + assign vsync_porch_start = 5; + assign vsync_porch_size = 5; + assign invert = 0; + end + 95, 105, 97, 107: begin + assign frame_width = 4400; + assign frame_height = 2250; + assign screen_width = 3840; + assign screen_height = 2160; + assign hsync_porch_start = 176; + assign hsync_porch_size = 88; + assign vsync_porch_start = 8; + assign vsync_porch_size = 10; + assign invert = 0; + end + endcase + assign hsync = invert ^ ((cx >= (screen_width + hsync_porch_start)) && (cx < ((screen_width + hsync_porch_start) + hsync_porch_size))); + assign vsync = invert ^ ((cy >= (screen_height + vsync_porch_start)) && (cy < ((screen_height + vsync_porch_start) + vsync_porch_size))); + endgenerate + localparam real VIDEO_RATE = (VIDEO_ID_CODE == 1 ? 25.2E6 : ((VIDEO_ID_CODE == 2) || (VIDEO_ID_CODE == 3) ? 27.027E6 : (VIDEO_ID_CODE == 4 ? 74.25E6 : (VIDEO_ID_CODE == 16 ? 148.5E6 : ((VIDEO_ID_CODE == 17) || (VIDEO_ID_CODE == 18) ? 27E6 : (VIDEO_ID_CODE == 19 ? 74.25E6 : (VIDEO_ID_CODE == 34 ? 74.25E6 : ((((VIDEO_ID_CODE == 95) || (VIDEO_ID_CODE == 105)) || (VIDEO_ID_CODE == 97)) || (VIDEO_ID_CODE == 107) ? 594E6 : 0)))))))) * ((VIDEO_REFRESH_RATE == 59.94) || (VIDEO_REFRESH_RATE == 29.97) ? 1000.0 / 1001.0 : 1); + always @(posedge clk_pixel) + if (reset) begin + cx <= sv2v_cast_C479B_signed(0); + cy <= sv2v_cast_4D393_signed(0); + end + else begin + cx <= (cx == (frame_width - 1'b1) ? sv2v_cast_C479B_signed(0) : cx + 1'b1); + cy <= (cx == (frame_width - 1'b1) ? (cy == (frame_height - 1'b1) ? sv2v_cast_4D393_signed(0) : cy + 1'b1) : cy); + end + reg video_data_period = 0; + always @(posedge clk_pixel) + if (reset) + video_data_period <= 0; + else + video_data_period <= (cx < screen_width) && (cy < screen_height); + reg [2:0] mode = 3'd1; + reg [23:0] video_data = 24'd0; + reg [5:0] control_data = 6'd0; + reg [11:0] data_island_data = 12'd0; + generate + if (!DVI_OUTPUT) begin : true_hdmi_output + reg video_guard = 1; + reg video_preamble = 0; + always @(posedge clk_pixel) + if (reset) begin + video_guard <= 1; + video_preamble <= 0; + end + else begin + video_guard <= ((cx >= (frame_width - 2)) && (cx < frame_width)) && ((cy == (frame_height - 1)) || (cy < screen_height)); + video_preamble <= ((cx >= (frame_width - 10)) && (cx < (frame_width - 2))) && ((cy == (frame_height - 1)) || (cy < screen_height)); + end + reg signed [31:0] max_num_packets_alongside; + reg [4:0] num_packets_alongside; + function automatic signed [4:0] sv2v_cast_5_signed; + input reg signed [4:0] inp; + sv2v_cast_5_signed = inp; + endfunction + always @(*) begin + max_num_packets_alongside = ((frame_width - screen_width) - 34) / 32; + if (max_num_packets_alongside > 18) + num_packets_alongside = 5'd18; + else + num_packets_alongside = sv2v_cast_5_signed(max_num_packets_alongside); + end + wire data_island_period_instantaneous; + assign data_island_period_instantaneous = ((num_packets_alongside > 0) && (cx >= (screen_width + 10))) && (cx < ((screen_width + 10) + (num_packets_alongside * 32))); + wire packet_enable; + function automatic [4:0] sv2v_cast_5; + input reg [4:0] inp; + sv2v_cast_5 = inp; + endfunction + assign packet_enable = data_island_period_instantaneous && (sv2v_cast_5((cx + screen_width) + 22) == 5'd0); + reg data_island_guard = 0; + reg data_island_preamble = 0; + reg data_island_period = 0; + always @(posedge clk_pixel) + if (reset) begin + data_island_guard <= 0; + data_island_preamble <= 0; + data_island_period <= 0; + end + else begin + data_island_guard <= (num_packets_alongside > 0) && (((cx >= (screen_width + 8)) && (cx < (screen_width + 10))) || ((cx >= ((screen_width + 10) + (num_packets_alongside * 32))) && (cx < (((screen_width + 10) + (num_packets_alongside * 32)) + 2)))); + data_island_preamble <= ((num_packets_alongside > 0) && (cx >= screen_width)) && (cx < (screen_width + 8)); + data_island_period <= data_island_period_instantaneous; + end + wire [23:0] header; + wire [223:0] sub; + wire video_field_end; + assign video_field_end = (cx == (screen_width - 1'b1)) && (cy == (screen_height - 1'b1)); + wire [4:0] packet_pixel_counter; + packet_picker #( + .VIDEO_ID_CODE(VIDEO_ID_CODE), + .VIDEO_RATE(VIDEO_RATE), + .AUDIO_RATE(AUDIO_RATE), + .AUDIO_BIT_WIDTH(AUDIO_BIT_WIDTH), + .VENDOR_NAME(VENDOR_NAME), + .PRODUCT_DESCRIPTION(PRODUCT_DESCRIPTION), + .SOURCE_DEVICE_INFORMATION(SOURCE_DEVICE_INFORMATION) + ) packet_picker( + .clk_pixel(clk_pixel), + .clk_audio(clk_audio), + .reset(reset), + .video_field_end(video_field_end), + .packet_enable(packet_enable), + .packet_pixel_counter(packet_pixel_counter), + .audio_sample_word(audio_sample_word), + .header(header), + .sub(sub) + ); + wire [8:0] packet_data; + packet_assembler packet_assembler( + .clk_pixel(clk_pixel), + .reset(reset), + .data_island_period(data_island_period), + .header(header), + .sub(sub), + .packet_data(packet_data), + .counter(packet_pixel_counter) + ); + always @(posedge clk_pixel) + if (reset) begin + mode <= 3'd2; + video_data <= 24'd0; + control_data = 6'd0; + data_island_data <= 12'd0; + end + else begin + mode <= (data_island_guard ? 3'd4 : (data_island_period ? 3'd3 : (video_guard ? 3'd2 : (video_data_period ? 3'd1 : 3'd0)))); + video_data <= rgb; + control_data <= {{1'b0, data_island_preamble}, {1'b0, video_preamble || data_island_preamble}, {vsync, hsync}}; + data_island_data[11:4] <= packet_data[8:1]; + data_island_data[3] <= cx != 0; + data_island_data[2] <= packet_data[0]; + data_island_data[1:0] <= {vsync, hsync}; + end + end + else always @(posedge clk_pixel) + if (reset) begin + mode <= 3'd0; + video_data <= 24'd0; + control_data <= 6'd0; + end + else begin + mode <= (video_data_period ? 3'd1 : 3'd0); + video_data <= rgb; + control_data <= {4'b0000, {vsync, hsync}}; + end + endgenerate + wire [29:0] tmds_internal; + genvar i; + generate + for (i = 0; i < NUM_CHANNELS; i = i + 1) begin : tmds_gen + tmds_channel #(.CN(i)) tmds_channel( + .clk_pixel(clk_pixel), + .video_data(video_data[(i * 8) + 7:i * 8]), + .data_island_data(data_island_data[(i * 4) + 3:i * 4]), + .control_data(control_data[(i * 2) + 1:i * 2]), + .mode(mode), + .tmds(tmds_internal[i * 10+:10]) + ); + end + endgenerate + serializer #( + .NUM_CHANNELS(NUM_CHANNELS), + .VIDEO_RATE(VIDEO_RATE) + ) serializer( + .clk_pixel(clk_pixel), + .clk_pixel_x5(clk_pixel_x5), + .reset(reset), + .tmds_internal(tmds_internal), + .tmds(tmds), + .tmds_clock(tmds_clock) + ); +endmodule diff --git a/src/packet_assembler.sv b/src/packet_assembler.sv deleted file mode 100644 index f0acbaa..0000000 --- a/src/packet_assembler.sv +++ /dev/null @@ -1,86 +0,0 @@ -// Implementation of HDMI packet ECC calculation. -// By Sameer Puri https://github.com/sameer - -module packet_assembler ( - input logic clk_pixel, - input logic reset, - input logic data_island_period, - input logic [23:0] header, // See Table 5-8 Packet Types - input logic [55:0] sub [3:0], - output logic [8:0] packet_data, // See Figure 5-4 Data Island Packet and ECC Structure - output logic [4:0] counter = 5'd0 -); - -// 32 pixel wrap-around counter. See Section 5.2.3.4 for further information. -always_ff @(posedge clk_pixel) -begin - if (reset) - counter <= 5'd0; - else if (data_island_period) - counter <= counter + 5'd1; -end -// BCH packets 0 to 3 are transferred two bits at a time, see Section 5.2.3.4 for further information. -wire [5:0] counter_t2 = {counter, 1'b0}; -wire [5:0] counter_t2_p1 = {counter, 1'b1}; - -// Initialize parity bits to 0 -logic [7:0] parity [4:0] = '{8'd0, 8'd0, 8'd0, 8'd0, 8'd0}; - -wire [63:0] bch [3:0]; -assign bch[0] = {parity[0], sub[0]}; -assign bch[1] = {parity[1], sub[1]}; -assign bch[2] = {parity[2], sub[2]}; -assign bch[3] = {parity[3], sub[3]}; -wire [31:0] bch4 = {parity[4], header}; -assign packet_data = {bch[3][counter_t2_p1], bch[2][counter_t2_p1], bch[1][counter_t2_p1], bch[0][counter_t2_p1], bch[3][counter_t2], bch[2][counter_t2], bch[1][counter_t2], bch[0][counter_t2], bch4[counter]}; - -// See Figure 5-5 Error Correction Code generator. Generalization of a CRC with binary BCH. -// See https://web.archive.org/web/20190520020602/http://hamsterworks.co.nz/mediawiki/index.php/Minimal_HDMI#Computing_the_ECC for an explanation of the implementation. -// See https://en.wikipedia.org/wiki/BCH_code#Systematic_encoding:_The_message_as_a_prefix for further information. -function automatic [7:0] next_ecc; -input [7:0] ecc, next_bch_bit; -begin - next_ecc = (ecc >> 1) ^ ((ecc[0] ^ next_bch_bit) ? 8'b10000011 : 8'd0); -end -endfunction - -logic [7:0] parity_next [4:0]; - -// The parity needs to be calculated 2 bits at a time for blocks 0 to 3. -// There's 56 bits being sent 2 bits at a time over TMDS channels 1 & 2, so the parity bits wouldn't be ready in time otherwise. -logic [7:0] parity_next_next [3:0]; - -genvar i; -generate - for(i = 0; i < 5; i++) - begin: parity_calc - if (i == 4) - assign parity_next[i] = next_ecc(parity[i], header[counter]); - else - begin - assign parity_next[i] = next_ecc(parity[i], sub[i][counter_t2]); - assign parity_next_next[i] = next_ecc(parity_next[i], sub[i][counter_t2_p1]); - end - end -endgenerate - -always_ff @(posedge clk_pixel) -begin - if (reset) - parity <= '{8'd0, 8'd0, 8'd0, 8'd0, 8'd0}; - else if (data_island_period) - begin - if (counter < 5'd28) // Compute ECC only on subpacket data, not on itself - begin - parity[3:0] <= parity_next_next; - if (counter < 5'd24) // Header only has 24 bits, whereas subpackets have 56 and 56 / 2 = 28. - parity[4] <= parity_next[4]; - end - else if (counter == 5'd31) - parity <= '{8'd0, 8'd0, 8'd0, 8'd0, 8'd0}; // Reset ECC for next packet - end - else - parity <= '{8'd0, 8'd0, 8'd0, 8'd0, 8'd0}; -end - -endmodule diff --git a/src/packet_assembler.v b/src/packet_assembler.v new file mode 100644 index 0000000..6b41daa --- /dev/null +++ b/src/packet_assembler.v @@ -0,0 +1,65 @@ +module packet_assembler ( + clk_pixel, + reset, + data_island_period, + header, + sub, + packet_data, + counter +); + input wire clk_pixel; + input wire reset; + input wire data_island_period; + input wire [23:0] header; + input wire [223:0] sub; + output wire [8:0] packet_data; + output reg [4:0] counter = 5'd0; + always @(posedge clk_pixel) + if (reset) + counter <= 5'd0; + else if (data_island_period) + counter <= counter + 5'd1; + wire [5:0] counter_t2 = {counter, 1'b0}; + wire [5:0] counter_t2_p1 = {counter, 1'b1}; + reg [39:0] parity = 40'h0000000000; + wire [63:0] bch [3:0]; + assign bch[0] = {parity[0+:8], sub[0+:56]}; + assign bch[1] = {parity[8+:8], sub[56+:56]}; + assign bch[2] = {parity[16+:8], sub[112+:56]}; + assign bch[3] = {parity[24+:8], sub[168+:56]}; + wire [31:0] bch4 = {parity[32+:8], header}; + assign packet_data = {bch[3][counter_t2_p1], bch[2][counter_t2_p1], bch[1][counter_t2_p1], bch[0][counter_t2_p1], bch[3][counter_t2], bch[2][counter_t2], bch[1][counter_t2], bch[0][counter_t2], bch4[counter]}; + function automatic [7:0] next_ecc; + input reg [7:0] ecc; + input reg [7:0] next_bch_bit; + next_ecc = (ecc >> 1) ^ (ecc[0] ^ next_bch_bit ? 8'b10000011 : 8'd0); + endfunction + wire [7:0] parity_next [4:0]; + wire [31:0] parity_next_next; + genvar i; + generate + for (i = 0; i < 5; i = i + 1) begin : parity_calc + if (i == 4) begin + assign parity_next[i] = next_ecc(parity[i * 8+:8], header[counter]); + end + else begin + assign parity_next[i] = next_ecc(parity[i * 8+:8], sub[(i * 56) + counter_t2]); + assign parity_next_next[i * 8+:8] = next_ecc(parity_next[i], sub[(i * 56) + counter_t2_p1]); + end + end + endgenerate + always @(posedge clk_pixel) + if (reset) + parity <= 40'h0000000000; + else if (data_island_period) begin + if (counter < 5'd28) begin + parity[0+:32] <= parity_next_next; + if (counter < 5'd24) + parity[32+:8] <= parity_next[4]; + end + else if (counter == 5'd31) + parity <= 40'h0000000000; + end + else + parity <= 40'h0000000000; +endmodule diff --git a/src/packet_picker.sv b/src/packet_picker.sv deleted file mode 100644 index 6be242b..0000000 --- a/src/packet_picker.sv +++ /dev/null @@ -1,193 +0,0 @@ -// Implementation of HDMI packet choice logic. -// By Sameer Puri https://github.com/sameer - -module packet_picker -#( - parameter int VIDEO_ID_CODE = 4, - parameter real VIDEO_RATE = 0, - parameter int AUDIO_BIT_WIDTH = 0, - parameter int AUDIO_RATE = 0, - parameter bit [8*8-1:0] VENDOR_NAME = 0, - parameter bit [8*16-1:0] PRODUCT_DESCRIPTION = 0, - parameter bit [7:0] SOURCE_DEVICE_INFORMATION = 0 -) -( - input logic clk_pixel, - input logic clk_audio, - input logic reset, - input logic video_field_end, - input logic packet_enable, - input logic [4:0] packet_pixel_counter, - input logic [AUDIO_BIT_WIDTH-1:0] audio_sample_word [1:0], - output logic [23:0] header, - output logic [55:0] sub [3:0] -); - -// Connect the current packet type's data to the output. -logic [7:0] packet_type = 8'd0; -logic [23:0] headers [255:0]; -logic [55:0] subs [255:0] [3:0]; -assign header = headers[packet_type]; -assign sub[0] = subs[packet_type][0]; -assign sub[1] = subs[packet_type][1]; -assign sub[2] = subs[packet_type][2]; -assign sub[3] = subs[packet_type][3]; - -// NULL packet -// "An HDMI Sink shall ignore bytes HB1 and HB2 of the Null Packet Header and all bytes of the Null Packet Body." -`ifdef MODEL_TECH -assign headers[0] = {8'd0, 8'd0, 8'd0}; assign subs[0] = '{56'd0, 56'd0, 56'd0, 56'd0}; -`else -assign headers[0] = {8'dX, 8'dX, 8'd0}; -assign subs[0][0] = 56'dX; -assign subs[0][1] = 56'dX; -assign subs[0][2] = 56'dX; -assign subs[0][3] = 56'dX; -`endif - -// Audio Clock Regeneration Packet -logic clk_audio_counter_wrap; -audio_clock_regeneration_packet #(.VIDEO_RATE(VIDEO_RATE), .AUDIO_RATE(AUDIO_RATE)) audio_clock_regeneration_packet (.clk_pixel(clk_pixel), .clk_audio(clk_audio), .clk_audio_counter_wrap(clk_audio_counter_wrap), .header(headers[1]), .sub(subs[1])); - -// Audio Sample packet -localparam bit [3:0] SAMPLING_FREQUENCY = AUDIO_RATE == 32000 ? 4'b0011 - : AUDIO_RATE == 44100 ? 4'b0000 - : AUDIO_RATE == 88200 ? 4'b1000 - : AUDIO_RATE == 176400 ? 4'b1100 - : AUDIO_RATE == 48000 ? 4'b0010 - : AUDIO_RATE == 96000 ? 4'b1010 - : AUDIO_RATE == 192000 ? 4'b1110 - : 4'bXXXX; -localparam int AUDIO_BIT_WIDTH_COMPARATOR = AUDIO_BIT_WIDTH < 20 ? 20 : AUDIO_BIT_WIDTH == 20 ? 25 : AUDIO_BIT_WIDTH < 24 ? 24 : AUDIO_BIT_WIDTH == 24 ? 29 : -1; -localparam bit [2:0] WORD_LENGTH = 3'(AUDIO_BIT_WIDTH_COMPARATOR - AUDIO_BIT_WIDTH); -localparam bit WORD_LENGTH_LIMIT = AUDIO_BIT_WIDTH <= 20 ? 1'b0 : 1'b1; - -logic [AUDIO_BIT_WIDTH-1:0] audio_sample_word_transfer [1:0]; -logic audio_sample_word_transfer_control = 1'd0; -always_ff @(posedge clk_audio) -begin - audio_sample_word_transfer <= audio_sample_word; - audio_sample_word_transfer_control <= !audio_sample_word_transfer_control; -end - -logic [1:0] audio_sample_word_transfer_control_synchronizer_chain = 2'd0; -always_ff @(posedge clk_pixel) - audio_sample_word_transfer_control_synchronizer_chain <= {audio_sample_word_transfer_control, audio_sample_word_transfer_control_synchronizer_chain[1]}; - -logic sample_buffer_current = 1'b0; -logic [1:0] samples_remaining = 2'd0; -logic [23:0] audio_sample_word_buffer [1:0] [3:0] [1:0]; -logic [AUDIO_BIT_WIDTH-1:0] audio_sample_word_transfer_mux [1:0]; -always_comb -begin - if (audio_sample_word_transfer_control_synchronizer_chain[0] ^ audio_sample_word_transfer_control_synchronizer_chain[1]) - audio_sample_word_transfer_mux = audio_sample_word_transfer; - else - audio_sample_word_transfer_mux = '{audio_sample_word_buffer[sample_buffer_current][samples_remaining][1][23:(24-AUDIO_BIT_WIDTH)], audio_sample_word_buffer[sample_buffer_current][samples_remaining][0][23:(24-AUDIO_BIT_WIDTH)]}; -end - -logic sample_buffer_used = 1'b0; -logic sample_buffer_ready = 1'b0; - -always_ff @(posedge clk_pixel) -begin - if (sample_buffer_used) - sample_buffer_ready <= 1'b0; - - if (audio_sample_word_transfer_control_synchronizer_chain[0] ^ audio_sample_word_transfer_control_synchronizer_chain[1]) - begin - audio_sample_word_buffer[sample_buffer_current][samples_remaining][0] <= {audio_sample_word_transfer_mux[0], (24-AUDIO_BIT_WIDTH)'(0)}; - audio_sample_word_buffer[sample_buffer_current][samples_remaining][1] <= {audio_sample_word_transfer_mux[1], (24-AUDIO_BIT_WIDTH)'(0)}; - if (samples_remaining == 2'd3) - begin - samples_remaining <= 2'd0; - sample_buffer_ready <= 1'b1; - sample_buffer_current <= !sample_buffer_current; - end - else - samples_remaining <= samples_remaining + 1'd1; - end -end - -logic [23:0] audio_sample_word_packet [3:0] [1:0]; -logic [3:0] audio_sample_word_present_packet; - -logic [7:0] frame_counter = 8'd0; -int k; -always_ff @(posedge clk_pixel) -begin - if (reset) - begin - frame_counter <= 8'd0; - end - else if (packet_pixel_counter == 5'd31 && packet_type == 8'h02) // Keep track of current IEC 60958 frame - begin - frame_counter = frame_counter + 8'd4; - if (frame_counter >= 8'd192) - frame_counter = frame_counter - 8'd192; - end -end -audio_sample_packet #(.SAMPLING_FREQUENCY(SAMPLING_FREQUENCY), .WORD_LENGTH({{WORD_LENGTH[0], WORD_LENGTH[1], WORD_LENGTH[2]}, WORD_LENGTH_LIMIT})) audio_sample_packet (.frame_counter(frame_counter), .valid_bit('{2'b00, 2'b00, 2'b00, 2'b00}), .user_data_bit('{2'b00, 2'b00, 2'b00, 2'b00}), .audio_sample_word(audio_sample_word_packet), .audio_sample_word_present(audio_sample_word_present_packet), .header(headers[2]), .sub(subs[2])); - - -auxiliary_video_information_info_frame #(.VIDEO_ID_CODE(7'(VIDEO_ID_CODE))) auxiliary_video_information_info_frame(.header(headers[130]), .sub(subs[130])); - - -source_product_description_info_frame #(.VENDOR_NAME(VENDOR_NAME), .PRODUCT_DESCRIPTION(PRODUCT_DESCRIPTION), .SOURCE_DEVICE_INFORMATION(SOURCE_DEVICE_INFORMATION)) source_product_description_info_frame(.header(headers[131]), .sub(subs[131])); - - -audio_info_frame audio_info_frame(.header(headers[132]), .sub(subs[132])); - - -// "A Source shall always transmit... [an InfoFrame] at least once per two Video Fields" -logic audio_info_frame_sent = 1'b0; -logic auxiliary_video_information_info_frame_sent = 1'b0; -logic source_product_description_info_frame_sent = 1'b0; -logic last_clk_audio_counter_wrap = 1'b0; -always_ff @(posedge clk_pixel) -begin - if (sample_buffer_used) - sample_buffer_used <= 1'b0; - - if (reset || video_field_end) - begin - audio_info_frame_sent <= 1'b0; - auxiliary_video_information_info_frame_sent <= 1'b0; - source_product_description_info_frame_sent <= 1'b0; - packet_type <= 8'dx; - end - else if (packet_enable) - begin - if (last_clk_audio_counter_wrap ^ clk_audio_counter_wrap) - begin - packet_type <= 8'd1; - last_clk_audio_counter_wrap <= clk_audio_counter_wrap; - end - else if (sample_buffer_ready) - begin - packet_type <= 8'd2; - audio_sample_word_packet <= audio_sample_word_buffer[!sample_buffer_current]; - audio_sample_word_present_packet <= 4'b1111; - sample_buffer_used <= 1'b1; - end - else if (!audio_info_frame_sent) - begin - packet_type <= 8'h84; - audio_info_frame_sent <= 1'b1; - end - else if (!auxiliary_video_information_info_frame_sent) - begin - packet_type <= 8'h82; - auxiliary_video_information_info_frame_sent <= 1'b1; - end - else if (!source_product_description_info_frame_sent) - begin - packet_type <= 8'h83; - source_product_description_info_frame_sent <= 1'b1; - end - else - packet_type <= 8'd0; - end -end - -endmodule diff --git a/src/packet_picker.v b/src/packet_picker.v new file mode 100644 index 0000000..579a29f --- /dev/null +++ b/src/packet_picker.v @@ -0,0 +1,206 @@ +module packet_picker ( + clk_pixel, + clk_audio, + reset, + video_field_end, + packet_enable, + packet_pixel_counter, + audio_sample_word, + header, + sub +); + parameter signed [31:0] VIDEO_ID_CODE = 4; + parameter real VIDEO_RATE = 0; + parameter signed [31:0] AUDIO_BIT_WIDTH = 0; + parameter signed [31:0] AUDIO_RATE = 0; + parameter [63:0] VENDOR_NAME = 0; + parameter [127:0] PRODUCT_DESCRIPTION = 0; + parameter [7:0] SOURCE_DEVICE_INFORMATION = 0; + input wire clk_pixel; + input wire clk_audio; + input wire reset; + input wire video_field_end; + input wire packet_enable; + input wire [4:0] packet_pixel_counter; + input wire [(2 * AUDIO_BIT_WIDTH) - 1:0] audio_sample_word; + output wire [23:0] header; + output wire [223:0] sub; + reg [7:0] packet_type = 8'd0; + wire [23:0] headers [255:0]; + wire [223:0] subs [255:0]; + assign header = headers[packet_type]; + assign sub = subs[packet_type]; + assign headers[0] = 24'bxxxxxxxxxxxxxxxx00000000; + assign subs[0] = 224'dx; + wire clk_audio_counter_wrap; + audio_clock_regeneration_packet #( + .VIDEO_RATE(VIDEO_RATE), + .AUDIO_RATE(AUDIO_RATE) + ) audio_clock_regeneration_packet( + .clk_pixel(clk_pixel), + .clk_audio(clk_audio), + .clk_audio_counter_wrap(clk_audio_counter_wrap), + .header(headers[1]), + .sub(subs[1]) + ); + localparam [3:0] SAMPLING_FREQUENCY = (AUDIO_RATE == 32000 ? 4'b0011 : (AUDIO_RATE == 44100 ? 4'b0000 : (AUDIO_RATE == 88200 ? 4'b1000 : (AUDIO_RATE == 176400 ? 4'b1100 : (AUDIO_RATE == 48000 ? 4'b0010 : (AUDIO_RATE == 96000 ? 4'b1010 : (AUDIO_RATE == 192000 ? 4'b1110 : 4'bxxxx))))))); + localparam signed [31:0] AUDIO_BIT_WIDTH_COMPARATOR = (AUDIO_BIT_WIDTH < 20 ? 20 : (AUDIO_BIT_WIDTH == 20 ? 25 : (AUDIO_BIT_WIDTH < 24 ? 24 : (AUDIO_BIT_WIDTH == 24 ? 29 : -1)))); + function automatic signed [2:0] sv2v_cast_3_signed; + input reg signed [2:0] inp; + sv2v_cast_3_signed = inp; + endfunction + localparam [2:0] WORD_LENGTH = sv2v_cast_3_signed(AUDIO_BIT_WIDTH_COMPARATOR - AUDIO_BIT_WIDTH); + localparam [0:0] WORD_LENGTH_LIMIT = (AUDIO_BIT_WIDTH <= 20 ? 1'b0 : 1'b1); + reg [(2 * AUDIO_BIT_WIDTH) - 1:0] audio_sample_word_transfer; + reg audio_sample_word_transfer_control = 1'd0; + always @(posedge clk_audio) begin + audio_sample_word_transfer <= audio_sample_word; + audio_sample_word_transfer_control <= !audio_sample_word_transfer_control; + end + reg [1:0] audio_sample_word_transfer_control_synchronizer_chain = 2'd0; + always @(posedge clk_pixel) audio_sample_word_transfer_control_synchronizer_chain <= {audio_sample_word_transfer_control, audio_sample_word_transfer_control_synchronizer_chain[1]}; + reg sample_buffer_current = 1'b0; + reg [1:0] samples_remaining = 2'd0; + reg [24*2*4*2-1:0] audio_sample_word_buffer; + wire [191:0] audio_sample_word_buffer_current; + assign audio_sample_word_buffer_current = sample_buffer_current ? audio_sample_word_buffer[383:192] : audio_sample_word_buffer[191:0]; + wire [47:0] audo_sample_word_buffer_current_samples_remaining; + assign audo_sample_word_buffer_current_samples_remaining = samples_remaining == 2'd3 ? audio_sample_word_buffer_current[191:144] : samples_remaining == 2'd2 ? audio_sample_word_buffer_current[143:96] : samples_remaining == 2'd1 ? audio_sample_word_buffer_current[95:48] : audio_sample_word_buffer_current[47:0]; + reg [(2 * AUDIO_BIT_WIDTH) - 1:0] audio_sample_word_transfer_mux; + always @(*) + if (audio_sample_word_transfer_control_synchronizer_chain[0] ^ audio_sample_word_transfer_control_synchronizer_chain[1]) + audio_sample_word_transfer_mux = audio_sample_word_transfer; + else + audio_sample_word_transfer_mux = {audo_sample_word_buffer_current_samples_remaining[47:48 - AUDIO_BIT_WIDTH], audo_sample_word_buffer_current_samples_remaining[23:24 - AUDIO_BIT_WIDTH]}; + reg sample_buffer_used = 1'b0; + reg sample_buffer_ready = 1'b0; + function automatic signed [(24 - AUDIO_BIT_WIDTH) - 1:0] sv2v_cast_6EABB_signed; + input reg signed [(24 - AUDIO_BIT_WIDTH) - 1:0] inp; + sv2v_cast_6EABB_signed = inp; + endfunction + always @(posedge clk_pixel) begin + if (sample_buffer_used) + sample_buffer_ready <= 1'b0; + if (audio_sample_word_transfer_control_synchronizer_chain[0] ^ audio_sample_word_transfer_control_synchronizer_chain[1]) begin + if (sample_buffer_current) begin + if (samples_remaining == 2'd0) begin + audio_sample_word_buffer[47:24] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[23:0] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end else if (samples_remaining == 2'd1) begin + audio_sample_word_buffer[95:72] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[71:48] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end else if (samples_remaining == 2'd2) begin + audio_sample_word_buffer[143:120] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[119:96] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end else if (samples_remaining == 2'd3) begin + audio_sample_word_buffer[191:168] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[167:144] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end + end else begin + if (samples_remaining == 2'd0) begin + audio_sample_word_buffer[239:216] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[215:192] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end else if (samples_remaining == 2'd1) begin + audio_sample_word_buffer[287:264] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[263:240] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end else if (samples_remaining == 2'd2) begin + audio_sample_word_buffer[335:312] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[311:288] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end else if (samples_remaining == 2'd3) begin + audio_sample_word_buffer[383:360] <= {audio_sample_word_transfer_mux[0+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + audio_sample_word_buffer[359:336] <= {audio_sample_word_transfer_mux[AUDIO_BIT_WIDTH+:AUDIO_BIT_WIDTH], sv2v_cast_6EABB_signed(0)}; + end + end + if (samples_remaining == 2'd3) begin + samples_remaining <= 2'd0; + sample_buffer_ready <= 1'b1; + sample_buffer_current <= !sample_buffer_current; + end + else + samples_remaining <= samples_remaining + 1'd1; + end + end + reg [191:0] audio_sample_word_packet; + reg [3:0] audio_sample_word_present_packet; + reg [7:0] frame_counter = 8'd0; + reg signed [31:0] k; + always @(posedge clk_pixel) + if (reset) + frame_counter <= 8'd0; + else if ((packet_pixel_counter == 5'd31) && (packet_type == 8'h02)) begin + frame_counter = frame_counter + 8'd4; + if (frame_counter >= 8'd192) + frame_counter = frame_counter - 8'd192; + end + audio_sample_packet #( + .SAMPLING_FREQUENCY(SAMPLING_FREQUENCY), + .WORD_LENGTH({{WORD_LENGTH[0], WORD_LENGTH[1], WORD_LENGTH[2]}, WORD_LENGTH_LIMIT}) + ) audio_sample_packet( + .frame_counter(frame_counter), + .valid_bit(8'b00000000), + .user_data_bit(8'b00000000), + .audio_sample_word(audio_sample_word_packet), + .audio_sample_word_present(audio_sample_word_present_packet), + .header(headers[2]), + .sub(subs[2]) + ); + function automatic signed [6:0] sv2v_cast_7_signed; + input reg signed [6:0] inp; + sv2v_cast_7_signed = inp; + endfunction + auxiliary_video_information_info_frame #(.VIDEO_ID_CODE(sv2v_cast_7_signed(VIDEO_ID_CODE))) auxiliary_video_information_info_frame( + .header(headers[130]), + .sub(subs[130]) + ); + source_product_description_info_frame #( + .VENDOR_NAME(VENDOR_NAME), + .PRODUCT_DESCRIPTION(PRODUCT_DESCRIPTION), + .SOURCE_DEVICE_INFORMATION(SOURCE_DEVICE_INFORMATION) + ) source_product_description_info_frame( + .header(headers[131]), + .sub(subs[131]) + ); + audio_info_frame audio_info_frame( + .header(headers[132]), + .sub(subs[132]) + ); + reg audio_info_frame_sent = 1'b0; + reg auxiliary_video_information_info_frame_sent = 1'b0; + reg source_product_description_info_frame_sent = 1'b0; + reg last_clk_audio_counter_wrap = 1'b0; + always @(posedge clk_pixel) begin + if (sample_buffer_used) + sample_buffer_used <= 1'b0; + if (reset || video_field_end) begin + audio_info_frame_sent <= 1'b0; + auxiliary_video_information_info_frame_sent <= 1'b0; + source_product_description_info_frame_sent <= 1'b0; + packet_type <= 8'bxxxxxxxx; + end + else if (packet_enable) + if (last_clk_audio_counter_wrap ^ clk_audio_counter_wrap) begin + packet_type <= 8'd1; + last_clk_audio_counter_wrap <= clk_audio_counter_wrap; + end + else if (sample_buffer_ready) begin + packet_type <= 8'd2; + audio_sample_word_packet <= audio_sample_word_buffer_current; + audio_sample_word_present_packet <= 4'b1111; + sample_buffer_used <= 1'b1; + end + else if (!audio_info_frame_sent) begin + packet_type <= 8'h84; + audio_info_frame_sent <= 1'b1; + end + else if (!auxiliary_video_information_info_frame_sent) begin + packet_type <= 8'h82; + auxiliary_video_information_info_frame_sent <= 1'b1; + end + else if (!source_product_description_info_frame_sent) begin + packet_type <= 8'h83; + source_product_description_info_frame_sent <= 1'b1; + end + else + packet_type <= 8'd0; + end +endmodule diff --git a/src/serializer.sv b/src/serializer.sv deleted file mode 100644 index b303c5d..0000000 --- a/src/serializer.sv +++ /dev/null @@ -1,247 +0,0 @@ -module serializer -#( - parameter int NUM_CHANNELS = 3, - parameter real VIDEO_RATE -) -( - input logic clk_pixel, - input logic clk_pixel_x5, - input logic reset, - input logic [9:0] tmds_internal [NUM_CHANNELS-1:0], - output logic [2:0] tmds, - output logic tmds_clock -); - -`ifndef VERILATOR - `ifdef SYNTHESIS - `ifndef ALTERA_RESERVED_QIS - // https://www.xilinx.com/support/documentation/user_guides/ug471_7Series_SelectIO.pdf - logic tmds_plus_clock [NUM_CHANNELS:0]; - assign tmds_plus_clock = '{tmds_clock, tmds[2], tmds[1], tmds[0]}; - logic [9:0] tmds_internal_plus_clock [NUM_CHANNELS:0]; - assign tmds_internal_plus_clock = '{10'b0000011111, tmds_internal[2], tmds_internal[1], tmds_internal[0]}; - logic [1:0] cascade [NUM_CHANNELS:0]; - - // this is requried for OSERDESE2 to work - logic internal_reset = 1'b1; - always @(posedge clk_pixel) - begin - internal_reset <= 1'b0; - end - genvar i; - generate - for (i = 0; i <= NUM_CHANNELS; i++) - begin: xilinx_serialize - OSERDESE2 #( - .DATA_RATE_OQ("DDR"), - .DATA_RATE_TQ("SDR"), - .DATA_WIDTH(10), - .SERDES_MODE("MASTER"), - .TRISTATE_WIDTH(1), - .TBYTE_CTL("FALSE"), - .TBYTE_SRC("FALSE") - ) primary ( - .OQ(tmds_plus_clock[i]), - .OFB(), - .TQ(), - .TFB(), - .SHIFTOUT1(), - .SHIFTOUT2(), - .TBYTEOUT(), - .CLK(clk_pixel_x5), - .CLKDIV(clk_pixel), - .D1(tmds_internal_plus_clock[i][0]), - .D2(tmds_internal_plus_clock[i][1]), - .D3(tmds_internal_plus_clock[i][2]), - .D4(tmds_internal_plus_clock[i][3]), - .D5(tmds_internal_plus_clock[i][4]), - .D6(tmds_internal_plus_clock[i][5]), - .D7(tmds_internal_plus_clock[i][6]), - .D8(tmds_internal_plus_clock[i][7]), - .TCE(1'b0), - .OCE(1'b1), - .TBYTEIN(1'b0), - .RST(reset || internal_reset), - .SHIFTIN1(cascade[i][0]), - .SHIFTIN2(cascade[i][1]), - .T1(1'b0), - .T2(1'b0), - .T3(1'b0), - .T4(1'b0) - ); - OSERDESE2 #( - .DATA_RATE_OQ("DDR"), - .DATA_RATE_TQ("SDR"), - .DATA_WIDTH(10), - .SERDES_MODE("SLAVE"), - .TRISTATE_WIDTH(1), - .TBYTE_CTL("FALSE"), - .TBYTE_SRC("FALSE") - ) secondary ( - .OQ(), - .OFB(), - .TQ(), - .TFB(), - .SHIFTOUT1(cascade[i][0]), - .SHIFTOUT2(cascade[i][1]), - .TBYTEOUT(), - .CLK(clk_pixel_x5), - .CLKDIV(clk_pixel), - .D1(1'b0), - .D2(1'b0), - .D3(tmds_internal_plus_clock[i][8]), - .D4(tmds_internal_plus_clock[i][9]), - .D5(1'b0), - .D6(1'b0), - .D7(1'b0), - .D8(1'b0), - .TCE(1'b0), - .OCE(1'b1), - .TBYTEIN(1'b0), - .RST(reset || internal_reset), - .SHIFTIN1(1'b0), - .SHIFTIN2(1'b0), - .T1(1'b0), - .T2(1'b0), - .T3(1'b0), - .T4(1'b0) - ); - end - endgenerate - `endif - `else - logic [9:0] tmds_reversed [NUM_CHANNELS-1:0]; - genvar i, j; - generate - for (i = 0; i < NUM_CHANNELS; i++) - begin: tmds_rev - for (j = 0; j < 10; j++) - begin: tmds_rev_channel - assign tmds_reversed[i][j] = tmds_internal[i][9-j]; - end - end - endgenerate - `ifdef MODEL_TECH - logic [3:0] position = 4'd0; - always_ff @(posedge clk_pixel_x5) - begin - tmds <= {tmds_reversed[2][position], tmds_reversed[1][position], tmds_reversed[0][position]}; - tmds_clock <= position >= 4'd5; - position <= position == 4'd9 ? 4'd0 : position + 1'd1; - end - always_ff @(negedge clk_pixel_x5) - begin - tmds <= {tmds_reversed[2][position], tmds_reversed[1][position], tmds_reversed[0][position]}; - tmds_clock <= position >= 4'd5; - position <= position == 4'd9 ? 4'd0 : position + 1'd1; - end - `else - `ifdef ALTERA_RESERVED_QIS - altlvds_tx ALTLVDS_TX_component ( - .tx_in ({10'b1111100000, tmds_reversed[2], tmds_reversed[1], tmds_reversed[0]}), - .tx_inclock (clk_pixel_x5), - .tx_out ({tmds_clock, tmds[2], tmds[1], tmds[0]}), - .tx_outclock (), - .pll_areset (1'b0), - .sync_inclock (1'b0), - .tx_coreclock (), - .tx_data_reset (reset), - .tx_enable (1'b1), - .tx_locked (), - .tx_pll_enable (1'b1), - .tx_syncclock (clk_pixel)); - defparam - ALTLVDS_TX_component.center_align_msb = "UNUSED", - ALTLVDS_TX_component.common_rx_tx_pll = "OFF", - ALTLVDS_TX_component.coreclock_divide_by = 1, - // ALTLVDS_TX_component.data_rate = "800.0 Mbps", - ALTLVDS_TX_component.deserialization_factor = 10, - ALTLVDS_TX_component.differential_drive = 0, - ALTLVDS_TX_component.enable_clock_pin_mode = "UNUSED", - ALTLVDS_TX_component.implement_in_les = "OFF", - ALTLVDS_TX_component.inclock_boost = 0, - ALTLVDS_TX_component.inclock_data_alignment = "EDGE_ALIGNED", - ALTLVDS_TX_component.inclock_period = int'(10000000.0 / (VIDEO_RATE * 10.0)), - ALTLVDS_TX_component.inclock_phase_shift = 0, - // ALTLVDS_TX_component.intended_device_family = "Cyclone V", - ALTLVDS_TX_component.lpm_hint = "CBX_MODULE_PREFIX=altlvds_tx_inst", - ALTLVDS_TX_component.lpm_type = "altlvds_tx", - ALTLVDS_TX_component.multi_clock = "OFF", - ALTLVDS_TX_component.number_of_channels = 4, - // ALTLVDS_TX_component.outclock_alignment = "EDGE_ALIGNED", - // ALTLVDS_TX_component.outclock_divide_by = 1, - // ALTLVDS_TX_component.outclock_duty_cycle = 50, - // ALTLVDS_TX_component.outclock_multiply_by = 1, - // ALTLVDS_TX_component.outclock_phase_shift = 0, - // ALTLVDS_TX_component.outclock_resource = "Dual-Regional clock", - ALTLVDS_TX_component.output_data_rate = int'(VIDEO_RATE * 10.0), - ALTLVDS_TX_component.pll_compensation_mode = "AUTO", - ALTLVDS_TX_component.pll_self_reset_on_loss_lock = "OFF", - ALTLVDS_TX_component.preemphasis_setting = 0, - // ALTLVDS_TX_component.refclk_frequency = "20.000000 MHz", - ALTLVDS_TX_component.registered_input = "OFF", - ALTLVDS_TX_component.use_external_pll = "ON", - ALTLVDS_TX_component.use_no_phase_shift = "ON", - ALTLVDS_TX_component.vod_setting = 0, - ALTLVDS_TX_component.clk_src_is_pll = "off"; - `else - // We don't know what the platform is so the best bet is an IP-less implementation. - // Shift registers are loaded with a set of values from tmds_channels every clk_pixel. - // They are shifted out on clk_pixel_x5 by the time the next set is loaded. - logic [9:0] tmds_shift [NUM_CHANNELS-1:0] = '{10'd0, 10'd0, 10'd0}; - - logic tmds_control = 1'd0; - always_ff @(posedge clk_pixel) - tmds_control <= !tmds_control; - - logic [3:0] tmds_control_synchronizer_chain = 4'd0; - always_ff @(posedge clk_pixel_x5) - tmds_control_synchronizer_chain <= {tmds_control, tmds_control_synchronizer_chain[3:1]}; - - logic load; - assign load = tmds_control_synchronizer_chain[1] ^ tmds_control_synchronizer_chain[0]; - logic [9:0] tmds_mux [NUM_CHANNELS-1:0]; - always_comb - begin - if (load) - tmds_mux = tmds_internal; - else - tmds_mux = tmds_shift; - end - - // See Section 5.4.1 - for (i = 0; i < NUM_CHANNELS; i++) - begin: tmds_shifting - always_ff @(posedge clk_pixel_x5) - tmds_shift[i] <= load ? tmds_mux[i] : tmds_shift[i] >> 2; - end - - logic [9:0] tmds_shift_clk_pixel = 10'b0000011111; - always_ff @(posedge clk_pixel_x5) - tmds_shift_clk_pixel <= load ? 10'b0000011111 : {tmds_shift_clk_pixel[1:0], tmds_shift_clk_pixel[9:2]}; - - logic [NUM_CHANNELS-1:0] tmds_shift_negedge_temp; - for (i = 0; i < NUM_CHANNELS; i++) - begin: tmds_driving - always_ff @(posedge clk_pixel_x5) - begin - tmds[i] <= tmds_shift[i][0]; - tmds_shift_negedge_temp[i] <= tmds_shift[i][1]; - end - always_ff @(negedge clk_pixel_x5) - tmds[i] <= tmds_shift_negedge_temp[i]; - end - logic tmds_clock_negedge_temp; - always_ff @(posedge clk_pixel_x5) - begin - tmds_clock <= tmds_shift_clk_pixel[0]; - tmds_clock_negedge_temp <= tmds_shift_clk_pixel[1]; - end - always_ff @(negedge clk_pixel_x5) - tmds_clock <= tmds_shift_negedge_temp; - - `endif - `endif - `endif -`endif -endmodule \ No newline at end of file diff --git a/src/serializer.v b/src/serializer.v new file mode 100644 index 0000000..f264aa8 --- /dev/null +++ b/src/serializer.v @@ -0,0 +1,63 @@ +module serializer ( + clk_pixel, + clk_pixel_x5, + reset, + tmds_internal, + tmds, + tmds_clock +); + parameter signed [31:0] NUM_CHANNELS = 3; + parameter real VIDEO_RATE = 0; + input wire clk_pixel; + input wire clk_pixel_x5; + input wire reset; + input wire [(NUM_CHANNELS * 10) - 1:0] tmds_internal; + output reg [2:0] tmds; + output reg tmds_clock; + wire [9:0] tmds_reversed [NUM_CHANNELS - 1:0]; + genvar i; + genvar j; + generate + for (i = 0; i < NUM_CHANNELS; i = i + 1) begin : tmds_rev + for (j = 0; j < 10; j = j + 1) begin : tmds_rev_channel + assign tmds_reversed[i][j] = tmds_internal[(i * 10) + (9 - j)]; + end + end + endgenerate + reg [(NUM_CHANNELS * 10) - 1:0] tmds_shift = 30'h00000000; + reg tmds_control = 1'd0; + always @(posedge clk_pixel) tmds_control <= !tmds_control; + reg [3:0] tmds_control_synchronizer_chain = 4'd0; + always @(posedge clk_pixel_x5) tmds_control_synchronizer_chain <= {tmds_control, tmds_control_synchronizer_chain[3:1]}; + wire load; + assign load = tmds_control_synchronizer_chain[1] ^ tmds_control_synchronizer_chain[0]; + reg [(NUM_CHANNELS * 10) - 1:0] tmds_mux; + always @(*) + if (load) + tmds_mux = tmds_internal; + else + tmds_mux = tmds_shift; + generate + for (i = 0; i < NUM_CHANNELS; i = i + 1) begin : tmds_shifting + always @(posedge clk_pixel_x5) tmds_shift[i * 10+:10] <= (load ? tmds_mux[i * 10+:10] : tmds_shift[i * 10+:10] >> 2); + end + endgenerate + reg [9:0] tmds_shift_clk_pixel = 10'b0000011111; + always @(posedge clk_pixel_x5) tmds_shift_clk_pixel <= (load ? 10'b0000011111 : {tmds_shift_clk_pixel[1:0], tmds_shift_clk_pixel[9:2]}); + reg [NUM_CHANNELS - 1:0] tmds_shift_negedge_temp; + generate + for (i = 0; i < NUM_CHANNELS; i = i + 1) begin : tmds_driving + always @(posedge clk_pixel_x5) begin + tmds[i] <= tmds_shift[i * 10]; + tmds_shift_negedge_temp[i] <= tmds_shift[(i * 10) + 1]; + end + always @(negedge clk_pixel_x5) tmds[i] <= tmds_shift_negedge_temp[i]; + end + endgenerate + reg tmds_clock_negedge_temp; + always @(posedge clk_pixel_x5) begin + tmds_clock <= tmds_shift_clk_pixel[0]; + tmds_clock_negedge_temp <= tmds_shift_clk_pixel[1]; + end + always @(negedge clk_pixel_x5) tmds_clock <= tmds_shift_negedge_temp; +endmodule diff --git a/src/source_product_description_info_frame.sv b/src/source_product_description_info_frame.sv deleted file mode 100644 index 42821cb..0000000 --- a/src/source_product_description_info_frame.sv +++ /dev/null @@ -1,64 +0,0 @@ -// Implementation of HDMI SPD InfoFrame packet. -// By Sameer Puri https://github.com/sameer - -// See CEA-861-D Section 6.5 page 72 (84 in PDF) -module source_product_description_info_frame -#( - parameter bit [8*8-1:0] VENDOR_NAME = 0, - parameter bit [8*16-1:0] PRODUCT_DESCRIPTION = 0, - parameter bit [7:0] SOURCE_DEVICE_INFORMATION = 0 -) -( - output logic [23:0] header, - output logic [55:0] sub [3:0] -); - -localparam bit [4:0] LENGTH = 5'd25; -localparam bit [7:0] VERSION = 8'd1; -localparam bit [6:0] TYPE = 7'd3; - -assign header = {{3'b0, LENGTH}, VERSION, {1'b1, TYPE}}; - -// PB0-PB6 = sub0 -// PB7-13 = sub1 -// PB14-20 = sub2 -// PB21-27 = sub3 -logic [7:0] packet_bytes [27:0]; - -assign packet_bytes[0] = 8'd1 + ~(header[23:16] + header[15:8] + header[7:0] + packet_bytes[24] + packet_bytes[23] + packet_bytes[22] + packet_bytes[21] + packet_bytes[20] + packet_bytes[19] + packet_bytes[18] + packet_bytes[17] + packet_bytes[16] + packet_bytes[15] + packet_bytes[14] + packet_bytes[13] + packet_bytes[12] + packet_bytes[11] + packet_bytes[10] + packet_bytes[9] + packet_bytes[8] + packet_bytes[7] + packet_bytes[6] + packet_bytes[5] + packet_bytes[4] + packet_bytes[3] + packet_bytes[2] + packet_bytes[1]); - - -byte vendor_name [0:7]; -byte product_description [0:15]; - -genvar i; -generate - for (i = 0; i < 8; i++) - begin: vendor_to_bytes - assign vendor_name[i] = VENDOR_NAME[(7-i+1)*8-1:(7-i)*8]; - end - for (i = 0; i < 16; i++) - begin: product_to_bytes - assign product_description[i] = PRODUCT_DESCRIPTION[(15-i+1)*8-1:(15-i)*8]; - end - - for (i = 1; i < 9; i++) - begin: pb_vendor - assign packet_bytes[i] = vendor_name[i - 1] == 8'h30 ? 8'h00 : vendor_name[i - 1]; - end - for (i = 9; i < LENGTH; i++) - begin: pb_product - assign packet_bytes[i] = product_description[i - 9] == 8'h30 ? 8'h00 : product_description[i - 9]; - end - assign packet_bytes[LENGTH] = SOURCE_DEVICE_INFORMATION; - for (i = 26; i < 28; i++) - begin: pb_reserved - assign packet_bytes[i] = 8'd0; - end - for (i = 0; i < 4; i++) - begin: pb_to_sub - assign sub[i] = {packet_bytes[6 + i*7], packet_bytes[5 + i*7], packet_bytes[4 + i*7], packet_bytes[3 + i*7], packet_bytes[2 + i*7], packet_bytes[1 + i*7], packet_bytes[0 + i*7]}; - end -endgenerate - -endmodule diff --git a/src/source_product_description_info_frame.v b/src/source_product_description_info_frame.v new file mode 100644 index 0000000..a228fbe --- /dev/null +++ b/src/source_product_description_info_frame.v @@ -0,0 +1,44 @@ +module source_product_description_info_frame ( + header, + sub +); + parameter [63:0] VENDOR_NAME = 0; + parameter [127:0] PRODUCT_DESCRIPTION = 0; + parameter [7:0] SOURCE_DEVICE_INFORMATION = 0; + output wire [23:0] header; + output wire [223:0] sub; + localparam [4:0] LENGTH = 5'd25; + localparam [7:0] VERSION = 8'd1; + localparam [6:0] TYPE = 7'd3; + assign header = {{3'b000, LENGTH}, VERSION, {1'b1, TYPE}}; + wire [7:0] packet_bytes [27:0]; + assign packet_bytes[0] = 8'd1 + ~((((((((((((((((((((((((((header[23:16] + header[15:8]) + header[7:0]) + packet_bytes[24]) + packet_bytes[23]) + packet_bytes[22]) + packet_bytes[21]) + packet_bytes[20]) + packet_bytes[19]) + packet_bytes[18]) + packet_bytes[17]) + packet_bytes[16]) + packet_bytes[15]) + packet_bytes[14]) + packet_bytes[13]) + packet_bytes[12]) + packet_bytes[11]) + packet_bytes[10]) + packet_bytes[9]) + packet_bytes[8]) + packet_bytes[7]) + packet_bytes[6]) + packet_bytes[5]) + packet_bytes[4]) + packet_bytes[3]) + packet_bytes[2]) + packet_bytes[1]); + reg signed [7:0] vendor_name [0:7]; + reg signed [7:0] product_description [0:15]; + genvar i; + generate + for (i = 0; i < 8; i = i + 1) begin : vendor_to_bytes + wire [((((8 - i) * 8) - 1) >= ((7 - i) * 8) ? ((((8 - i) * 8) - 1) - ((7 - i) * 8)) + 1 : (((7 - i) * 8) - (((8 - i) * 8) - 1)) + 1):1] sv2v_tmp_CD3AB; + assign sv2v_tmp_CD3AB = VENDOR_NAME[((8 - i) * 8) - 1:(7 - i) * 8]; + always @(*) vendor_name[i] = sv2v_tmp_CD3AB; + end + for (i = 0; i < 16; i = i + 1) begin : product_to_bytes + wire [((((16 - i) * 8) - 1) >= ((15 - i) * 8) ? ((((16 - i) * 8) - 1) - ((15 - i) * 8)) + 1 : (((15 - i) * 8) - (((16 - i) * 8) - 1)) + 1):1] sv2v_tmp_97D02; + assign sv2v_tmp_97D02 = PRODUCT_DESCRIPTION[((16 - i) * 8) - 1:(15 - i) * 8]; + always @(*) product_description[i] = sv2v_tmp_97D02; + end + for (i = 1; i < 9; i = i + 1) begin : pb_vendor + assign packet_bytes[i] = (vendor_name[i - 1] == 8'h30 ? 8'h00 : vendor_name[i - 1]); + end + for (i = 9; i < LENGTH; i = i + 1) begin : pb_product + assign packet_bytes[i] = (product_description[i - 9] == 8'h30 ? 8'h00 : product_description[i - 9]); + end + assign packet_bytes[LENGTH] = SOURCE_DEVICE_INFORMATION; + for (i = 26; i < 28; i = i + 1) begin : pb_reserved + assign packet_bytes[i] = 8'd0; + end + for (i = 0; i < 4; i = i + 1) begin : pb_to_sub + assign sub[i * 56+:56] = {packet_bytes[6 + (i * 7)], packet_bytes[5 + (i * 7)], packet_bytes[4 + (i * 7)], packet_bytes[3 + (i * 7)], packet_bytes[2 + (i * 7)], packet_bytes[1 + (i * 7)], packet_bytes[i * 7]}; + end + endgenerate +endmodule diff --git a/src/tmds_channel.sv b/src/tmds_channel.sv deleted file mode 100644 index 9400c16..0000000 --- a/src/tmds_channel.sv +++ /dev/null @@ -1,169 +0,0 @@ -// Implementation of HDMI Spec v1.4a Section 5.4: Encoding, Section 5.2.2.1: Video Guard Band, Section 5.2.3.3: Data Island Guard Bands. -// By Sameer Puri https://github.com/sameer - -module tmds_channel -#( - // TMDS Channel number. - // There are only 3 possible channel numbers in HDMI 1.4a: 0, 1, 2. - parameter int CN = 0 -) -( - input logic clk_pixel, - input logic [7:0] video_data, - input logic [3:0] data_island_data, - input logic [1:0] control_data, - input logic [2:0] mode, // Mode select (0 = control, 1 = video, 2 = video guard, 3 = island, 4 = island guard) - output logic [9:0] tmds = 10'b1101010100 -); - -// See Section 5.4.4.1 -// Below is a direct implementation of Figure 5-7, using the same variable names. - -logic signed [4:0] acc = 5'sd0; - -logic [8:0] q_m; -logic [9:0] q_out; -logic [9:0] video_coding; -assign video_coding = q_out; - -logic [3:0] N1D; -logic signed [4:0] N1q_m07; -logic signed [4:0] N0q_m07; -always_comb -begin - N1D = video_data[0] + video_data[1] + video_data[2] + video_data[3] + video_data[4] + video_data[5] + video_data[6] + video_data[7]; - case(q_m[0] + q_m[1] + q_m[2] + q_m[3] + q_m[4] + q_m[5] + q_m[6] + q_m[7]) - 4'b0000: N1q_m07 = 5'sd0; - 4'b0001: N1q_m07 = 5'sd1; - 4'b0010: N1q_m07 = 5'sd2; - 4'b0011: N1q_m07 = 5'sd3; - 4'b0100: N1q_m07 = 5'sd4; - 4'b0101: N1q_m07 = 5'sd5; - 4'b0110: N1q_m07 = 5'sd6; - 4'b0111: N1q_m07 = 5'sd7; - 4'b1000: N1q_m07 = 5'sd8; - default: N1q_m07 = 5'sd0; - endcase - N0q_m07 = 5'sd8 - N1q_m07; -end - -logic signed [4:0] acc_add; - -integer i; - -always_comb -begin - if (N1D > 4'd4 || (N1D == 4'd4 && video_data[0] == 1'd0)) - begin - q_m[0] = video_data[0]; - for(i = 0; i < 7; i++) - q_m[i + 1] = q_m[i] ~^ video_data[i + 1]; - q_m[8] = 1'b0; - end - else - begin - q_m[0] = video_data[0]; - for(i = 0; i < 7; i++) - q_m[i + 1] = q_m[i] ^ video_data[i + 1]; - q_m[8] = 1'b1; - end - if (acc == 5'sd0 || (N1q_m07 == N0q_m07)) - begin - if (q_m[8]) - begin - acc_add = N1q_m07 - N0q_m07; - q_out = {~q_m[8], q_m[8], q_m[7:0]}; - end - else - begin - acc_add = N0q_m07 - N1q_m07; - q_out = {~q_m[8], q_m[8], ~q_m[7:0]}; - end - end - else - begin - if ((acc > 5'sd0 && N1q_m07 > N0q_m07) || (acc < 5'sd0 && N1q_m07 < N0q_m07)) - begin - q_out = {1'b1, q_m[8], ~q_m[7:0]}; - acc_add = (N0q_m07 - N1q_m07) + (q_m[8] ? 5'sd2 : 5'sd0); - end - else - begin - q_out = {1'b0, q_m[8], q_m[7:0]}; - acc_add = (N1q_m07 - N0q_m07) - (~q_m[8] ? 5'sd2 : 5'sd0); - end - end -end - -always_ff @(posedge clk_pixel) acc <= mode != 3'd1 ? 5'sd0 : acc + acc_add; - -// See Section 5.4.2 -logic [9:0] control_coding; -always_comb -begin - unique case(control_data) - 2'b00: control_coding = 10'b1101010100; - 2'b01: control_coding = 10'b0010101011; - 2'b10: control_coding = 10'b0101010100; - 2'b11: control_coding = 10'b1010101011; - endcase -end - -// See Section 5.4.3 -logic [9:0] terc4_coding; -always_comb -begin - unique case(data_island_data) - 4'b0000 : terc4_coding = 10'b1010011100; - 4'b0001 : terc4_coding = 10'b1001100011; - 4'b0010 : terc4_coding = 10'b1011100100; - 4'b0011 : terc4_coding = 10'b1011100010; - 4'b0100 : terc4_coding = 10'b0101110001; - 4'b0101 : terc4_coding = 10'b0100011110; - 4'b0110 : terc4_coding = 10'b0110001110; - 4'b0111 : terc4_coding = 10'b0100111100; - 4'b1000 : terc4_coding = 10'b1011001100; - 4'b1001 : terc4_coding = 10'b0100111001; - 4'b1010 : terc4_coding = 10'b0110011100; - 4'b1011 : terc4_coding = 10'b1011000110; - 4'b1100 : terc4_coding = 10'b1010001110; - 4'b1101 : terc4_coding = 10'b1001110001; - 4'b1110 : terc4_coding = 10'b0101100011; - 4'b1111 : terc4_coding = 10'b1011000011; - endcase -end - -// See Section 5.2.2.1 -logic [9:0] video_guard_band; -generate - if (CN == 0 || CN == 2) - assign video_guard_band = 10'b1011001100; - else - assign video_guard_band = 10'b0100110011; -endgenerate - -// See Section 5.2.3.3 -logic [9:0] data_guard_band; -generate - if (CN == 1 || CN == 2) - assign data_guard_band = 10'b0100110011; - else - assign data_guard_band = control_data == 2'b00 ? 10'b1010001110 - : control_data == 2'b01 ? 10'b1001110001 - : control_data == 2'b10 ? 10'b0101100011 - : 10'b1011000011; -endgenerate - -// Apply selected mode. -always @(posedge clk_pixel) -begin - case (mode) - 3'd0: tmds <= control_coding; - 3'd1: tmds <= video_coding; - 3'd2: tmds <= video_guard_band; - 3'd3: tmds <= terc4_coding; - 3'd4: tmds <= data_guard_band; - endcase -end - -endmodule diff --git a/src/tmds_channel.v b/src/tmds_channel.v new file mode 100644 index 0000000..047aeee --- /dev/null +++ b/src/tmds_channel.v @@ -0,0 +1,125 @@ +module tmds_channel ( + clk_pixel, + video_data, + data_island_data, + control_data, + mode, + tmds +); + parameter signed [31:0] CN = 0; + input wire clk_pixel; + input wire [7:0] video_data; + input wire [3:0] data_island_data; + input wire [1:0] control_data; + input wire [2:0] mode; + output reg [9:0] tmds = 10'b1101010100; + reg signed [4:0] acc = 5'sd0; + reg [8:0] q_m; + reg [9:0] q_out; + wire [9:0] video_coding; + assign video_coding = q_out; + reg [3:0] N1D; + reg signed [4:0] N1q_m07; + reg signed [4:0] N0q_m07; + always @(*) begin + N1D = ((((((video_data[0] + video_data[1]) + video_data[2]) + video_data[3]) + video_data[4]) + video_data[5]) + video_data[6]) + video_data[7]; + case (((((((q_m[0] + q_m[1]) + q_m[2]) + q_m[3]) + q_m[4]) + q_m[5]) + q_m[6]) + q_m[7]) + 4'b0000: N1q_m07 = 5'sd0; + 4'b0001: N1q_m07 = 5'sd1; + 4'b0010: N1q_m07 = 5'sd2; + 4'b0011: N1q_m07 = 5'sd3; + 4'b0100: N1q_m07 = 5'sd4; + 4'b0101: N1q_m07 = 5'sd5; + 4'b0110: N1q_m07 = 5'sd6; + 4'b0111: N1q_m07 = 5'sd7; + 4'b1000: N1q_m07 = 5'sd8; + default: N1q_m07 = 5'sd0; + endcase + N0q_m07 = 5'sd8 - N1q_m07; + end + reg signed [4:0] acc_add; + integer i; + always @(*) begin + if ((N1D > 4'd4) || ((N1D == 4'd4) && (video_data[0] == 1'd0))) begin + q_m[0] = video_data[0]; + for (i = 0; i < 7; i = i + 1) + q_m[i + 1] = q_m[i] ~^ video_data[i + 1]; + q_m[8] = 1'b0; + end + else begin + q_m[0] = video_data[0]; + for (i = 0; i < 7; i = i + 1) + q_m[i + 1] = q_m[i] ^ video_data[i + 1]; + q_m[8] = 1'b1; + end + if ((acc == 5'sd0) || (N1q_m07 == N0q_m07)) begin + if (q_m[8]) begin + acc_add = N1q_m07 - N0q_m07; + q_out = {~q_m[8], q_m[8], q_m[7:0]}; + end + else begin + acc_add = N0q_m07 - N1q_m07; + q_out = {~q_m[8], q_m[8], ~q_m[7:0]}; + end + end + else if (((acc > 5'sd0) && (N1q_m07 > N0q_m07)) || ((acc < 5'sd0) && (N1q_m07 < N0q_m07))) begin + q_out = {1'b1, q_m[8], ~q_m[7:0]}; + acc_add = (N0q_m07 - N1q_m07) + (q_m[8] ? 5'sd2 : 5'sd0); + end + else begin + q_out = {1'b0, q_m[8], q_m[7:0]}; + acc_add = (N1q_m07 - N0q_m07) - (~q_m[8] ? 5'sd2 : 5'sd0); + end + end + always @(posedge clk_pixel) acc <= (mode != 3'd1 ? 5'sd0 : acc + acc_add); + reg [9:0] control_coding; + always @(*) + case (control_data) + 2'b00: control_coding = 10'b1101010100; + 2'b01: control_coding = 10'b0010101011; + 2'b10: control_coding = 10'b0101010100; + 2'b11: control_coding = 10'b1010101011; + endcase + reg [9:0] terc4_coding; + always @(*) + case (data_island_data) + 4'b0000: terc4_coding = 10'b1010011100; + 4'b0001: terc4_coding = 10'b1001100011; + 4'b0010: terc4_coding = 10'b1011100100; + 4'b0011: terc4_coding = 10'b1011100010; + 4'b0100: terc4_coding = 10'b0101110001; + 4'b0101: terc4_coding = 10'b0100011110; + 4'b0110: terc4_coding = 10'b0110001110; + 4'b0111: terc4_coding = 10'b0100111100; + 4'b1000: terc4_coding = 10'b1011001100; + 4'b1001: terc4_coding = 10'b0100111001; + 4'b1010: terc4_coding = 10'b0110011100; + 4'b1011: terc4_coding = 10'b1011000110; + 4'b1100: terc4_coding = 10'b1010001110; + 4'b1101: terc4_coding = 10'b1001110001; + 4'b1110: terc4_coding = 10'b0101100011; + 4'b1111: terc4_coding = 10'b1011000011; + endcase + wire [9:0] video_guard_band; + generate + if ((CN == 0) || (CN == 2)) begin + assign video_guard_band = 10'b1011001100; + end + else assign video_guard_band = 10'b0100110011; + endgenerate + wire [9:0] data_guard_band; + generate + if ((CN == 1) || (CN == 2)) begin + assign data_guard_band = 10'b0100110011; + end + else assign data_guard_band = (control_data == 2'b00 ? 10'b1010001110 : (control_data == 2'b01 ? 10'b1001110001 : (control_data == 2'b10 ? 10'b0101100011 : 10'b1011000011))); + endgenerate + always @(posedge clk_pixel) + case (mode) + 3'd0: tmds <= control_coding; + 3'd1: tmds <= video_coding; + 3'd2: tmds <= video_guard_band; + 3'd3: tmds <= terc4_coding; + 3'd4: tmds <= data_guard_band; + endcase +endmodule diff --git a/top/top.sv b/top/top.sv index 20ba9fd..f932c4e 100644 --- a/top/top.sv +++ b/top/top.sv @@ -9,9 +9,9 @@ logic reset; pll pll(.c0(clk_pixel_x5), .c1(clk_pixel), .c2(clk_audio)); -logic [15:0] audio_sample_word [1:0] = '{16'd0, 16'd0}; +logic [31:0] audio_sample_word = 32'd0; always @(posedge clk_audio) - audio_sample_word <= '{audio_sample_word[1] + 16'd1, audio_sample_word[0] - 16'd1}; + audio_sample_word <= {audio_sample_word[31:16] + 16'd1, audio_sample_word[15:0] - 16'd1}; logic [23:0] rgb = 24'd0; logic [9:0] cx, cy, screen_start_x, screen_start_y, frame_width, frame_height, screen_width, screen_height;