Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Xilinx support on master branch #20

Merged
merged 2 commits into from
Feb 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 18 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,7 @@ Most free and open source HDMI source (computer/gaming console) implementations
### Platform Support

- [x] Altera (tested on [MKR Vidor 4000](https://store.arduino.cc/usa/mkr-vidor-4000))
- [x] Xilinx
- Confirmed for [v1.1](https://github.com/hdl-util/hdmi/releases/tag/v1.1) by community
- Master will be tested soon using [Spartan Edge Accelerator Board](https://www.seeedstudio.com/Spartan-Edge-Accelerator-Board-p-4261.html)
- [x] Xilinx (tested on [Spartan Edge Accelerator Board](https://www.seeedstudio.com/Spartan-Edge-Accelerator-Board-p-4261.html))
- [ ] Lattice (unknown)

### To-do List (upon request)
Expand Down Expand Up @@ -61,23 +59,24 @@ Most free and open source HDMI source (computer/gaming console) implementations

You'll need to set up a PLL for producing the two HDMI clocks. The pixel clock for each supported format is shown below:

|Video Resolution|Video ID Code(s)|Refresh Rate|Pixel Clock Frequency|
|Video Resolution|Video ID Code(s)|Refresh Rate|Pixel Clock Frequency|[Progressive](https://en.wikipedia.org/wiki/Progressive_scan)/[Interlaced](https://en.wikipedia.org/wiki/Interlaced_video)|
|---|---|---|---|
|640x480|1|60Hz|25.2MHz|
|640x480|1|59.94Hz|25.175MHz|
|720x480|2, 3|60Hz|27.027MHz|
|720x480|2, 3|59.94Hz|27MHz|
|1280x720|4|60Hz|74.25MHz|
|1280x720|4|59.94Hz|74.176MHz|
|1920x1080|16|60Hz|148.5MHz|
|1920x1080|16|59.94Hz|148.352MHz|
|1920x1080|34|30Hz|74.25MHz|
|1920x1080|34|29.97Hz|74.176MHz|
|720x576|17, 18|50Hz|27MHz|
|1280x720|19|50Hz|74.25MHz|
|3840x2160|97, 107|60Hz|594MHz|

The second clock is a clock 5 times as fast as the pixel clock. Even if your FPGA only has a single PLL, the Altera MegaWizard (or the Xilinx equivalent) should still be able to produce both.
|640x480|1|60Hz|25.2MHz|P|
|640x480|1|59.94Hz|25.175MHz|P|
|720x480|2, 3|60Hz|27.027MHz|P|
|720x480|2, 3|59.94Hz|27MHz|P|
|720x576|17, 18|50Hz|27MHz|P|
|1280x720|4|60Hz|74.25MHz|P|
|1280x720|4|59.94Hz|74.176MHz|P|
|1280x720|19|50Hz|74.25MHz|P|
|1920x1080|16|60Hz|148.5MHz|P|
|1920x1080|16|59.94Hz|148.352MHz|P|
|1920x1080|34|30Hz|74.25MHz|P|
|1920x1080|34|29.97Hz|74.176MHz|P|
|3840x2160 (not ready)|97, 107|60Hz|594MHz|P|
|3840x2160|95, 105|30Hz|297MHz|P|

The second clock is a clock 5 times as fast as the pixel clock. Even if your FPGA only has a single PLL, the Altera MegaWizard (or the Xilinx equivalent) should still be able to produce both. See [hdl-util/hdmi-demo](https://github.com/hdl-util/hdmi-demo/) for example PLLs.

### L-PCM Audio Bitrate / Sampling Frequency

Expand Down
4 changes: 2 additions & 2 deletions src/hdmi.sv
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ generate
assign hsync = cx >= 440 && cx < 440 + 40;
assign vsync = cy >= 5 && cy < 5 + 5;
end
97, 107:
95, 105, 97, 107:
begin
assign frame_width = 4400;
assign frame_height = 2250;
Expand All @@ -147,7 +147,7 @@ localparam real VIDEO_RATE = (VIDEO_ID_CODE == 1 ? 25.2E6
: VIDEO_ID_CODE == 17 || VIDEO_ID_CODE == 18 ? 27E6
: VIDEO_ID_CODE == 19 ? 74.25E6
: VIDEO_ID_CODE == 34 ? 74.25E6
: VIDEO_ID_CODE == 97 || VIDEO_ID_CODE == 107 ? 594E6
: 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.
Expand Down
140 changes: 78 additions & 62 deletions src/serializer.sv
Original file line number Diff line number Diff line change
Expand Up @@ -14,74 +14,90 @@ module serializer
`ifndef VERILATOR
`ifdef SYNTHESIS
`ifndef ALTERA_RESERVED_QIS
// Based on VHDL implementation by Furkan Cayci, 2010
// 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 =tmds_internal_plus_clock '{10'b0000011111, tmds_internal[2], tmds_internal[1], tmds_internal[0]};
assign tmds_internal_plus_clock = '{10'b0000011111, tmds_internal[2], tmds_internal[1], tmds_internal[0]};
logic [1:0] cascade [NUM_CHANNELS:0];
genvar i;
generate
for (i = 0; i <= NUM_CHANNELS; i++)
begin: xilinx_serialize
OSERDES2 #(.DATA_RATE_OQ("DDR"), .DATA_RATE_TQ("SDR"), .DATA_WIDTH(10), .SERDES_MODE("MASTER"), .TRISTATE_WIDTH(1))
primary (
.OQ(i == NUM_CHANNELS ? tmds_clock : tmds[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(),
.SHIFTIN1(cascade[i][0]),
.SHIFTIN2(cascade[i][1]),
.T1(1'b0),
.T2(1'b0),
.T3(1'b0),
.T4(1'b0)
);
OSERDES2 #(.DATA_RATE_OQ("DDR"), .DATA_RATE_TQ("SDR"), .DATA_WIDTH(10), .SERDES_MODE("SLAVE"), .TRISTATE_WIDTH(1))
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(),
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0),
.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("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(1'b0),
.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(1'b0),
.SHIFTIN1(1'b0),
.SHIFTIN2(1'b0),
.T1(1'b0),
.T2(1'b0),
.T3(1'b0),
.T4(1'b0)
);
end
endgenerate
`endif
Expand Down