Bladeren bron

Merge in video generator changes

H. Peter Anvin 3 jaren geleden
bovenliggende
commit
d6d6aa5763
11 gewijzigde bestanden met toevoegingen van 266 en 216 verwijderingen
  1. 9 9
      fpga/ip/hdmitx.v
  2. 8 8
      fpga/ip/pll.v
  3. 11 55
      fpga/max80.sv
  4. BIN
      fpga/output_files/max80.jbc
  5. BIN
      fpga/output_files/max80.jic
  6. BIN
      fpga/output_files/max80.pof
  7. BIN
      fpga/output_files/max80.sof
  8. 79 110
      fpga/tmdsenc.sv
  9. 24 31
      fpga/transpose.sv
  10. 132 0
      fpga/video.sv
  11. 3 3
      fw/boot.mif

+ 9 - 9
fpga/ip/hdmitx.v

@@ -80,14 +80,14 @@ module hdmitx (
 		ALTLVDS_TX_component.center_align_msb = "UNUSED",
 		ALTLVDS_TX_component.common_rx_tx_pll = "ON",
 		ALTLVDS_TX_component.coreclock_divide_by = 2,
-		ALTLVDS_TX_component.data_rate = "480.0 Mbps",
+		ALTLVDS_TX_component.data_rate = "336.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 = "ON",
 		ALTLVDS_TX_component.inclock_boost = 0,
 		ALTLVDS_TX_component.inclock_data_alignment = "EDGE_ALIGNED",
-		ALTLVDS_TX_component.inclock_period = 20833,
+		ALTLVDS_TX_component.inclock_period = 14881,
 		ALTLVDS_TX_component.inclock_phase_shift = 0,
 		ALTLVDS_TX_component.intended_device_family = "Cyclone IV E",
 		ALTLVDS_TX_component.lpm_hint = "CBX_MODULE_PREFIX=hdmitx",
@@ -100,7 +100,7 @@ module hdmitx (
 		ALTLVDS_TX_component.outclock_multiply_by = 2,
 		ALTLVDS_TX_component.outclock_phase_shift = 0,
 		ALTLVDS_TX_component.outclock_resource = "AUTO",
-		ALTLVDS_TX_component.output_data_rate = 480,
+		ALTLVDS_TX_component.output_data_rate = 336,
 		ALTLVDS_TX_component.pll_compensation_mode = "AUTO",
 		ALTLVDS_TX_component.pll_self_reset_on_loss_lock = "ON",
 		ALTLVDS_TX_component.preemphasis_setting = 0,
@@ -121,15 +121,15 @@ endmodule
 // Retrieval info: PRIVATE: CNX_CLOCK_CHOICES STRING "tx_coreclock"
 // Retrieval info: PRIVATE: CNX_CLOCK_MODE NUMERIC "0"
 // Retrieval info: PRIVATE: CNX_COMMON_PLL NUMERIC "1"
-// Retrieval info: PRIVATE: CNX_DATA_RATE STRING "480.0"
+// Retrieval info: PRIVATE: CNX_DATA_RATE STRING "336.0"
 // Retrieval info: PRIVATE: CNX_DESER_FACTOR NUMERIC "10"
 // Retrieval info: PRIVATE: CNX_EXT_PLL STRING "OFF"
 // Retrieval info: PRIVATE: CNX_LE_SERDES STRING "ON"
 // Retrieval info: PRIVATE: CNX_NUM_CHANNEL NUMERIC "3"
 // Retrieval info: PRIVATE: CNX_OUTCLOCK_DIVIDE_BY NUMERIC "10"
 // Retrieval info: PRIVATE: CNX_PLL_ARESET NUMERIC "1"
-// Retrieval info: PRIVATE: CNX_PLL_FREQ STRING "48.00"
-// Retrieval info: PRIVATE: CNX_PLL_PERIOD STRING "20.833"
+// Retrieval info: PRIVATE: CNX_PLL_FREQ STRING "67.20"
+// Retrieval info: PRIVATE: CNX_PLL_PERIOD STRING "14.881"
 // Retrieval info: PRIVATE: CNX_REG_INOUT NUMERIC "1"
 // Retrieval info: PRIVATE: CNX_TX_CORECLOCK STRING "ON"
 // Retrieval info: PRIVATE: CNX_TX_LOCKED STRING "ON"
@@ -145,14 +145,14 @@ endmodule
 // Retrieval info: CONSTANT: COMMON_RX_TX_PLL STRING "ON"
 // Retrieval info: CONSTANT: CORECLOCK_DIVIDE_BY NUMERIC "2"
 // Retrieval info: CONSTANT: clk_src_is_pll STRING "off"
-// Retrieval info: CONSTANT: DATA_RATE STRING "480.0 Mbps"
+// Retrieval info: CONSTANT: DATA_RATE STRING "336.0 Mbps"
 // Retrieval info: CONSTANT: DESERIALIZATION_FACTOR NUMERIC "10"
 // Retrieval info: CONSTANT: DIFFERENTIAL_DRIVE NUMERIC "0"
 // Retrieval info: CONSTANT: ENABLE_CLOCK_PIN_MODE STRING "UNUSED"
 // Retrieval info: CONSTANT: IMPLEMENT_IN_LES STRING "ON"
 // Retrieval info: CONSTANT: INCLOCK_BOOST NUMERIC "0"
 // Retrieval info: CONSTANT: INCLOCK_DATA_ALIGNMENT STRING "EDGE_ALIGNED"
-// Retrieval info: CONSTANT: INCLOCK_PERIOD NUMERIC "20833"
+// Retrieval info: CONSTANT: INCLOCK_PERIOD NUMERIC "14881"
 // Retrieval info: CONSTANT: INCLOCK_PHASE_SHIFT NUMERIC "0"
 // Retrieval info: CONSTANT: INTENDED_DEVICE_FAMILY STRING "Cyclone IV E"
 // Retrieval info: CONSTANT: LPM_HINT STRING "UNUSED"
@@ -165,7 +165,7 @@ endmodule
 // Retrieval info: CONSTANT: OUTCLOCK_MULTIPLY_BY NUMERIC "2"
 // Retrieval info: CONSTANT: OUTCLOCK_PHASE_SHIFT NUMERIC "0"
 // Retrieval info: CONSTANT: OUTCLOCK_RESOURCE STRING "AUTO"
-// Retrieval info: CONSTANT: OUTPUT_DATA_RATE NUMERIC "480"
+// Retrieval info: CONSTANT: OUTPUT_DATA_RATE NUMERIC "336"
 // Retrieval info: CONSTANT: PLL_COMPENSATION_MODE STRING "AUTO"
 // Retrieval info: CONSTANT: PLL_SELF_RESET_ON_LOSS_LOCK STRING "ON"
 // Retrieval info: CONSTANT: PREEMPHASIS_SETTING NUMERIC "0"

+ 8 - 8
fpga/ip/pll.v

@@ -143,9 +143,9 @@ module pll (
 		altpll_component.clk1_duty_cycle = 50,
 		altpll_component.clk1_multiply_by = 7,
 		altpll_component.clk1_phase_shift = "0",
-		altpll_component.clk2_divide_by = 4,
+		altpll_component.clk2_divide_by = 5,
 		altpll_component.clk2_duty_cycle = 50,
-		altpll_component.clk2_multiply_by = 1,
+		altpll_component.clk2_multiply_by = 7,
 		altpll_component.clk2_phase_shift = "0",
 		altpll_component.clk3_divide_by = 5,
 		altpll_component.clk3_duty_cycle = 50,
@@ -232,7 +232,7 @@ endmodule
 // Retrieval info: PRIVATE: DEVICE_SPEED_GRADE STRING "Any"
 // Retrieval info: PRIVATE: DIV_FACTOR0 NUMERIC "2"
 // Retrieval info: PRIVATE: DIV_FACTOR1 NUMERIC "4"
-// Retrieval info: PRIVATE: DIV_FACTOR2 NUMERIC "4"
+// Retrieval info: PRIVATE: DIV_FACTOR2 NUMERIC "10"
 // Retrieval info: PRIVATE: DIV_FACTOR3 NUMERIC "5"
 // Retrieval info: PRIVATE: DIV_FACTOR4 NUMERIC "2"
 // Retrieval info: PRIVATE: DUTY_CYCLE0 STRING "50.00000000"
@@ -242,7 +242,7 @@ endmodule
 // Retrieval info: PRIVATE: DUTY_CYCLE4 STRING "50.00000000"
 // Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE0 STRING "168.000000"
 // Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE1 STRING "84.000000"
-// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE2 STRING "12.000000"
+// Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE2 STRING "67.199997"
 // Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE3 STRING "134.399994"
 // Retrieval info: PRIVATE: EFF_OUTPUT_FREQ_VALUE4 STRING "168.000000"
 // Retrieval info: PRIVATE: EXPLICIT_SWITCHOVER_COUNTER STRING "0"
@@ -279,13 +279,13 @@ endmodule
 // Retrieval info: PRIVATE: MIRROR_CLK4 STRING "0"
 // Retrieval info: PRIVATE: MULT_FACTOR0 NUMERIC "7"
 // Retrieval info: PRIVATE: MULT_FACTOR1 NUMERIC "7"
-// Retrieval info: PRIVATE: MULT_FACTOR2 NUMERIC "1"
+// Retrieval info: PRIVATE: MULT_FACTOR2 NUMERIC "14"
 // Retrieval info: PRIVATE: MULT_FACTOR3 NUMERIC "14"
 // Retrieval info: PRIVATE: MULT_FACTOR4 NUMERIC "7"
 // Retrieval info: PRIVATE: NORMAL_MODE_RADIO STRING "0"
 // Retrieval info: PRIVATE: OUTPUT_FREQ0 STRING "168.00000000"
 // Retrieval info: PRIVATE: OUTPUT_FREQ1 STRING "84.00000000"
-// Retrieval info: PRIVATE: OUTPUT_FREQ2 STRING "48.00000000"
+// Retrieval info: PRIVATE: OUTPUT_FREQ2 STRING "60.00000000"
 // Retrieval info: PRIVATE: OUTPUT_FREQ3 STRING "133.00000000"
 // Retrieval info: PRIVATE: OUTPUT_FREQ4 STRING "168.00000000"
 // Retrieval info: PRIVATE: OUTPUT_FREQ_MODE0 STRING "0"
@@ -362,9 +362,9 @@ endmodule
 // Retrieval info: CONSTANT: CLK1_DUTY_CYCLE NUMERIC "50"
 // Retrieval info: CONSTANT: CLK1_MULTIPLY_BY NUMERIC "7"
 // Retrieval info: CONSTANT: CLK1_PHASE_SHIFT STRING "0"
-// Retrieval info: CONSTANT: CLK2_DIVIDE_BY NUMERIC "4"
+// Retrieval info: CONSTANT: CLK2_DIVIDE_BY NUMERIC "5"
 // Retrieval info: CONSTANT: CLK2_DUTY_CYCLE NUMERIC "50"
-// Retrieval info: CONSTANT: CLK2_MULTIPLY_BY NUMERIC "1"
+// Retrieval info: CONSTANT: CLK2_MULTIPLY_BY NUMERIC "7"
 // Retrieval info: CONSTANT: CLK2_PHASE_SHIFT STRING "0"
 // Retrieval info: CONSTANT: CLK3_DIVIDE_BY NUMERIC "5"
 // Retrieval info: CONSTANT: CLK3_DUTY_CYCLE NUMERIC "50"

+ 11 - 55
fpga/max80.sv

@@ -180,61 +180,17 @@ module max80 (
      else
        vid_rst_n <= rst_n;
 
-   // HDMI - generate random data to give Quartus something to do
-   reg [23:0] dummydata = 30'hc8_fb87;
-
-   always @(posedge vid_clk)
-     dummydata <= { dummydata[22:0], dummydata[23] };
-
-   wire [7:0] hdmi_data[3];
-   wire [9:0] hdmi_tmds[3];
-   wire [29:0] hdmi_to_tx;
-
-   assign hdmi_data[0] = dummydata[7:0];
-   assign hdmi_data[1] = dummydata[15:8];
-   assign hdmi_data[2] = dummydata[23:16];
-
-   generate
-      genvar   i;
-      for (i = 0; i < 3; i = i + 1)
-	begin : hdmitmds
-	   tmdsenc enc (
-		    .rst_n ( vid_rst_n ),
-		    .clk ( vid_clk ),
-		    .den ( 1'b1 ),
-		    .d ( hdmi_data[i] ),
-		    .c ( 2'b00 ),
-		    .q ( hdmi_tmds[i] )
-		    );
-	end
-   endgenerate
-
-   assign hdmi_scl = 1'bz;
-   assign hdmi_sda = 1'bz;
-   assign hdmi_hpd = 1'bz;
-
-   //
-   // The ALTLVDS_TX megafunctions is MSB-first and in time-major order.
-   // However, TMDS is LSB-first, and we have three TMDS words that
-   // concatenate in word(channel)-major order.
-   //
-   transpose #(.words(3), .bits(10), .reverse_b(1),
-	       .reg_d(0), .reg_q(0)) hdmitranspose
-     (
-      .clk ( vid_clk ),
-      .d ( { hdmi_tmds[2], hdmi_tmds[1], hdmi_tmds[0] } ),
-      .q ( hdmi_to_tx )
-      );
-
-   hdmitx hdmitx (
-		  .pll_areset ( ~pll_locked[0] ),
-		  .tx_in ( hdmi_to_tx ),
-		  .tx_inclock ( vid_clk ),
-		  .tx_coreclock ( vid_hdmiclk ), // Pixel clock in HDMI domain
-		  .tx_locked ( pll_locked[1] ),
-		  .tx_out ( hdmi_d ),
-		  .tx_outclock ( hdmi_clk )
-		  );
+   // HDMI video interface
+   video video (
+		.rst_n      ( vid_rst_n ),
+		.vid_clk    ( vid_clk ),
+		.pll_locked ( pll_locked ),
+
+		.hdmi_d   ( hdmi_d ),
+		.hdmi_clk ( hdmi_clk ),
+		.hdmi_scl ( hdmi_scl ),
+		.hdmi_hpd ( hdmi_hpd )
+		);
 
    //
    // Internal CPU bus

BIN
fpga/output_files/max80.jbc


BIN
fpga/output_files/max80.jic


BIN
fpga/output_files/max80.pof


BIN
fpga/output_files/max80.sof


+ 79 - 110
fpga/tmdsenc.sv

@@ -2,100 +2,72 @@
 // Encodes a word in TMDS 8/10 format
 //
 
-`undef USE_TMDSROM
-
-`ifdef USE_TMDSROM
-module tmdsenc
-  (
-   input	rst_n,
-   input	clk,
-   input	den, // It is a data word, not a control word
-   input [7:0]	d, // Data word
-   input [1:0]	c, // Control word
-   output [9:0] q
-   );
-
-   reg signed [3:0]	     disparity; // Running disparity/2
-   reg [9:0]		     qreg;
-   assign q = qreg;
-
-   wire [15:0]		     romq;
-
-   tmdsrom tmdsrom (
-		    .clk ( clk ),
-		    .d ( d ),
-		    .q ( romq )
-		    );
-
-   // Delay two cycles to match tmdsrom
-   reg [1:0]		     denreg;
-   reg [1:0]		     creg[2];
-
-   wire invert =
-	(disparity > 4'sd0 & romq[10]) |
-	(disparity < 4'sd0 & romq[11]);
-
-   wire cp =  creg[1][0];
-   wire cn = ~creg[1][0];
-   wire cx = ~creg[1][0] ^ creg[1][1]; // XNOR of C1 and C0
-
-   always @(negedge rst_n or posedge clk)
-     if (~rst_n)
-       begin
-	  disparity <= 4'sd0;
-	  denreg    <= 2'b00;
-	  creg[0]   <= 2'b00;
-	  creg[1]   <= 2'b00;
-       end
-     else
-       begin
-	  denreg <= { denreg[0], den };
-	  creg[0] <= c;
-	  creg[1] <= creg[0];
-
-	  if (denreg[1])
-	    begin
-	       qreg <= romq[9:0] ^ (invert ? 10'h2ff : 10'h000);
-	       disparity <= invert ?
-			    disparity - romq[15:12] :
-			    disparity + romq[15:12];
-	    end
-	  else
-	    begin
-	       qreg <= { cx, {4{cn, cp}}, cp };
-	       disparity <= 4'd0;
-	    end // else: !if(den)
-       end // else: !if(~rst_n)
-endmodule // tmdsenc
-
-`else // not USE_TMDSROM
-
 module tmdsenc
   (
    input	rst_n,
    input	clk,
-   input	den, // It is a data word, not a control word
-   input [7:0]	d, // Data word
-   input [1:0]	c, // Control word
+   input	den,	// It is a data word, not a control word
+   input [7:0]	d,	// Data word
+   input	tercen, // Control data is TERC4 encoded
+   input [3:0]	c,	// Control or TERC4 word
    output [9:0] q
    );
 
-   reg signed [3:0] disparity; // Running disparity/2
-   reg [9:0]	    qreg = 10'b11010101000; // Symbol C0
+   // Bit 4 is TERC4 enable
+   function logic [9:0] csym(input tercen, input [4:0] sym);
+      casez ({tercen, sym})
+	// Plain TMDS control symbols
+	5'b0_??00: csym = 10'b11010_10100;
+	5'b0_??01: csym = 10'b00101_01011;
+	5'b0_??10: csym = 10'b01010_10100;
+	5'b0_??11: csym = 10'b10101_01011;
+
+	// TERC4 control symbols
+	5'b1_0000: csym = 10'b10100_11100;
+	5'b1_0001: csym = 10'b1001100011;
+	5'b1_0010: csym = 10'b1011100100;
+	5'b1_0011: csym = 10'b1011100010;
+	5'b1_0100: csym = 10'b0101110001;
+	5'b1_0101: csym = 10'b0100011110;
+	5'b1_0110: csym = 10'b0110001110;
+	5'b1_0111: csym = 10'b0100111100;
+	5'b1_1000: csym = 10'b1011001100;
+	5'b1_1001: csym = 10'b0100111001;
+	5'b1_1010: csym = 10'b0110011100;
+	5'b1_1011: csym = 10'b1011000110;
+	5'b1_1100: csym = 10'b1010001110;
+	5'b1_1101: csym = 10'b1001110001;
+	5'b1_1110: csym = 10'b0101100011;
+	5'b1_1111: csym = 10'b1011000011;
+      endcase // casez (sym)
+   endfunction // csym
+
+   function logic [9:0] bitrev10(input [9:0] in);
+      for (int i = 0; i < 10; i++)
+	bitrev10[i] = in[9-i];
+   endfunction // bitrev10
+
+   reg signed [4:0] disparity; // Running disparity/2
+   reg [9:0]	    qreg;
    assign q = qreg;
 
    reg [7:0]	    dreg;
    reg		    denreg;
-   reg [1:0]	    creg;
+   reg [3:0]	    creg;
+   reg		    tercenreg;
 
-   wire signed [2:0] delta =
-	((dreg[7] + dreg[6]) + (dreg[5] + dreg[4])) +
-	((dreg[3] + dreg[2]) + (dreg[1] + dreg[0])) - 3'sd4;
+   wire signed [3:0] ddisp =
+	dreg[7] + dreg[6] + dreg[5] + dreg[4] +
+	dreg[3] + dreg[2] + dreg[1] + dreg[0] - 'sd4;
 
    reg [8:0]	     dx;		// X(N)OR stage output
-   always @(*)
+   wire signed [3:0] xdisp =		// Does not include dx[8]!
+	dx[7] + dx[6] + dx[5] + dx[4] +
+	dx[3] + dx[2] + dx[1] + dx[0] - 'sd4;
+
+   always_comb
      begin
-	dx[8] = (delta > 3'sd0) | (~&delta & ~dreg[0]);
+	dx[8] = $signed(ddisp | { 3'd0, ~dreg[0] }) <= 4'sd0;
 	dx[0] = dreg[0];
 	dx[1] = dx[0] ^ dreg[1] ^ ~dx[8];
 	dx[2] = dx[1] ^ dreg[2] ^ ~dx[8];
@@ -106,50 +78,47 @@ module tmdsenc
 	dx[7] = dx[6] ^ dreg[7] ^ ~dx[8];
      end // always @ (*)
 
-   wire cp =  creg[0];
-   wire cn = ~creg[0];
-   wire cx = ~creg[0] ^ creg[1];	// XNOR of c[1] and c[0]
+   reg [9:0] dq;		// Disparity stage output
+   reg	     dispsign;		// Disparity counter up or down
+
+   always_comb
+     begin
+	dq[9]   = ((disparity == 5'sd0) | (xdisp == 4'sd0))
+	  ? ~dx[8] : ( disparity[4] == xdisp[3] );
+	dq[8]   = dx[8];
+	dq[7:0] = dx[7:0] ^ {{8{dq[9]}}};
+     end
+
+   wire signed [3:0] qdisp =
+	dq[9] + dq[8] + dq[7] + dq[6] + dq[5] +
+	dq[4] + dq[3] + dq[2] + dq[1] + dq[0] - 'sd5;
 
    always @(negedge rst_n or posedge clk)
      if (~rst_n)
        begin
-	  disparity <= 4'sd0;
-	  qreg      <= 10'b11010101000;
-	  dreg      <= 8'hxx;
+	  dreg      <= 'b0;
+	  disparity <= 'sd0;
+	  qreg      <= csym(1'b0, 4'b0000);
 	  denreg    <= 1'b0;
 	  creg      <= 2'b00;
+	  tercenreg <= 1'b0;
        end
      else
        begin
-	  denreg <= den;
-	  creg   <= c;
-	  dreg   <= d;
+	  denreg    <= den;
+	  tercenreg <= tercen;
+	  creg      <= c;
+	  dreg      <= d;
 
 	  if (denreg)
 	    begin
-	       if ( (disparity == 4'sd0) | (delta == 3'sd0) )
-		 begin
-		    qreg <= { ~dx[8], dx[8], dx[7:0] ^ ~{8{dx[8]}} };
-	            disparity <= dx[8] ?
-			    disparity + delta :
-			    disparity - delta;
-	         end
-	       else if ( disparity[3] ^ ~delta[2] )
-		 begin
-		    qreg <= { 1'b1, dx[8], ~dx[7:0] };
-		    disparity <= disparity - (delta - dx[8]);
-		 end
-	       else
-		 begin
-		    qreg <= { 1'b0, dx[8], dx[7:0] };
-		    disparity <= disparity + (delta - ~dx[8]);
-		 end
-	    end // if (den)
+	       qreg <= dq;
+	       disparity <= disparity + qdisp;
+	    end
 	  else
 	    begin
-	       qreg <= { cx, {4{cn, cp}}, cp };
-	       disparity <= 4'sd0;
-	    end // else: !if(den)
+	       qreg <= csym(tercenreg, creg);
+	       disparity <= 'sd0;
+	    end
        end // else: !if(~rst_n)
 endmodule // tmdsenc
-`endif

+ 24 - 31
fpga/transpose.sv

@@ -33,13 +33,13 @@ endmodule // condreg
 // this may be useful to parameterize other modules.
 //
 module transpose
-  #(parameter words,
-    parameter bits,
-    parameter reverse_w = 0,
-    parameter reverse_b = 0,
-    parameter reg_d     = 0,
-    parameter reg_q     = 0,
-    parameter transpose = 1)
+  #(parameter integer words,
+    parameter integer bits,
+    parameter [0:0] reverse_w = 1'b0,
+    parameter [0:0] reverse_b = 1'b0,
+    parameter [0:0] reg_d     = 1'b0,
+    parameter [0:0] reg_q     = 1'b0,
+    parameter [0:0] transpose = 1'b1)
    (
     input		    clk,
     input [words*bits-1:0]  d,
@@ -61,8 +61,8 @@ module transpose
 	  for (b = 0; b < bits; b = b + 1)
 	    begin
 	       integer ww, bb, ii, oo;
-	       ww = reverse_w ? words-w-1 : w;
-	       bb = reverse_b ? bits -b-1 : b;
+	       ww = reverse_w ? (words-1)-w : w;
+	       bb = reverse_b ? (bits -1)-b : b;
 	       ii = ww*bits+bb;
 	       oo = transpose ? b*words+w  : w*bits+b;
 	       out[oo] = in[ii];
@@ -75,34 +75,27 @@ endmodule // parameter
 //
 // If the parameter "reverse" is 0, then just pass through
 // the input; this may be useful to parameterize other modules.
+// This is just a special case of the transpose module.
 //
 module reverse
-  #(parameter bits,
-    parameter reg_d   = 0,
-    parameter reg_q   = 0,
-    parameter reverse = 1)
+  #(parameter integer bits,
+    parameter [0:0] reg_d   = 1'b0,
+    parameter [0:0] reg_q   = 1'b0,
+    parameter [0:0] reverse = 1'b1)
    (
     input	      clk,
     input [bits-1:0]  d,
     output [bits-1:0] q
     );
 
-   wire [bits-1:0]   in;
-   reg [bits-1:0]    out;
-
-   condreg #(.bits(bits), .register(reg_d))
-   dreg (.clk (clk), .d (d), .q(in));
-   condreg #(.bits(bits), .register(reg_q))
-   qreg (.clk (clk), .d (out), .q(q));
-
-   always @(*)
-     begin
-	integer i;
-	for (i = 0; i < bits; i = i + 1)
-	  begin
-	     integer ii;
-	     ii = reverse ? bits-i-1 : i;
-	     out[i] = in[ii];
-	  end
-     end
+   transpose #(
+	       .words     ( 1 ),
+	       .bits      ( bits ),
+	       .reverse_w ( 1'b0 ),
+	       .reverse_b ( reverse ),
+	       .reg_d     ( reg_d ),
+	       .reg_q     ( reg_q ),
+	       .transpose ( 1'b0 )
+	       )
+   rev ( .clk (clk), .d (d), .q (q) );
 endmodule // parameter

+ 132 - 0
fpga/video.sv

@@ -0,0 +1,132 @@
+module video (
+	      input	   rst_n,
+	      input	   vid_clk,
+	      input [1:0]  pll_locked,
+
+	      output [2:0] hdmi_d,
+	      output	   hdmi_clk,
+	      inout	   hdmi_scl,
+	      inout	   hdmi_sda,
+	      inout	   hdmi_hpd
+	      );
+
+   assign hdmi_scl = 1'bz;
+   assign hdmi_sda = 1'bz;
+   assign hdmi_hpd = 1'bz;
+
+   // 1024x768x60 with a 67.2 MHz pixel clock
+   // Htiming: 1024 128 112 140  = 1404
+   // Vtiming:  768   3   4  23  =  798
+   reg [10:0]	    x;
+   reg [ 9:0]	    y;
+
+   reg [7:0]	    r;
+   reg [7:0]	    g;
+   reg [7:0]	    b;
+   reg		    hblank;
+   reg		    hsync;
+   reg		    vblank;
+   reg		    vsync;
+
+   wire [7:0]	    pixbar = { x[6:0], 1'b0 } ^ {8{y[9]}};
+
+   always @(posedge vid_clk or negedge rst_n)
+     if (~rst_n)
+       begin
+	  x      <= 11'b0;
+	  y      <= 10'b0;
+	  r      <= 8'b0;
+	  g      <= 8'b0;
+	  b      <= 8'b0;
+
+	  hblank <= 1'b0;
+	  hsync  <= 1'b0;
+	  vblank <= 1'b0;
+	  vsync  <= 1'b0;
+       end
+     else
+       begin
+	  r <= pixbar & {8{x[9]}};
+	  g <= pixbar & {8{x[8]}};
+	  b <= pixbar & {8{x[7]}};
+
+	  x <= x + 1'b1;
+	  if (x >= 11'd1403)
+	    begin
+	       x <= 11'd0;
+	       y <= y + 1'b1;
+	       if (y >= 10'd797)
+		 y <= 10'd0;
+	    end
+
+	  hblank <= x[10];
+	  vblank <= &y[9:8];
+
+	  hsync  <= (x >= 11'd1152 && x < 11'd1264);
+	  vsync  <= (y >= 10'd771 && y < 10'd775);
+       end // else: !if(~rst_n)
+
+   wire [7:0] hdmi_data[0:2];
+
+   assign hdmi_data[2] = r;
+   assign hdmi_data[1] = g;
+   assign hdmi_data[0] = b;
+
+   // hdmi_ctl[4] enables TERC4 encoding
+   wire [4:0] hdmi_ctl[0:2];
+
+   assign hdmi_ctl[0][0]   = hsync;
+   assign hdmi_ctl[0][1]   = vsync;
+   assign hdmi_ctl[0][4:2] = 3'b0_00;
+   assign hdmi_ctl[1]      = 5'b0_0000;
+   assign hdmi_ctl[2]      = 5'b0_0000;
+
+   wire [9:0] hdmi_tmds_data[0:2]; // TMDS encoded data per channel
+
+   generate
+      genvar   i;
+      for (i = 0; i < 3; i = i + 1)
+	begin : hdmitmds
+	   tmdsenc enc (
+		    .rst_n ( rst_n ),
+		    .clk ( vid_clk ),
+		    .den ( ~hblank & ~vblank ),
+		    .d ( hdmi_data[i] ),
+		    .c ( hdmi_ctl[i][3:0] ),
+		    .tercen( hdmi_ctl[i][4] ),
+		    .q ( hdmi_tmds_data[i] )
+		    );
+	end
+   endgenerate
+
+   assign hdmi_scl = 1'bz;
+   assign hdmi_sda = 1'bz;
+   assign hdmi_hpd = 1'bz;
+
+   //
+   // The ALTLVDS_TX megafunctions is MSB-first and in time-major order.
+   // However, TMDS is LSB-first, and we have three TMDS words that
+   // concatenate in word(channel)-major order.
+   //
+   wire [29:0] hdmi_to_tx;	// TMDS data in the order hdmitx expects
+
+   transpose #(.words(3), .bits(10), .reverse_b(1),
+	       .reg_d(0), .reg_q(0)) hdmitranspose
+     (
+      .clk ( vid_clk ),
+      .d ( { hdmi_tmds_data[2], hdmi_tmds_data[1], hdmi_tmds_data[0] } ),
+      .q ( hdmi_to_tx )
+      );
+
+   wire        vid_hdmiclk;
+
+   hdmitx hdmitx (
+		  .pll_areset ( ~pll_locked[0] ),
+		  .tx_in ( hdmi_to_tx ),
+		  .tx_inclock ( vid_clk ),
+		  .tx_coreclock ( vid_hdmiclk ), // Pixel clock in HDMI domain
+		  .tx_locked ( pll_locked[1] ),
+		  .tx_out ( hdmi_d ),
+		  .tx_outclock ( hdmi_clk )
+		  );
+endmodule // video

+ 3 - 3
fw/boot.mif

@@ -6875,10 +6875,10 @@ CONTENT BEGIN
 1AD4 : 2064656C;
 1AD5 : 203A6E6F;
 1AD6 : 20766F4E;
-1AD7 : 32203720;
+1AD7 : 32203131;
 1AD8 : 20313230;
-1AD9 : 323A3232;
-1ADA : 31323A30;
+1AD9 : 313A3931;
+1ADA : 39353A32;
 1ADB : 5452000A;
 1ADC : 49203A43;
 1ADD : 53204332;