Skip to content

Commit 0b54441

Browse files
authored
Merge pull request #12 from projf/signed-coords
Signed coords
2 parents e5faa58 + 3c93cdd commit 0b54441

File tree

10 files changed

+91
-112
lines changed

10 files changed

+91
-112
lines changed

README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ The Project F display controller makes it easy to add video output to FPGA proje
44

55
To get started take a look at the [demos](#demos) then read [modules](doc/modules.md) for the display controller interfaces and parameters. The design aims to be as generic as possible but does make use of Xilinx Series 7 specific features, such as SerDes. If you want advice on adapting this design to other FPGAs, then take a look at [porting](doc/porting.md).
66

7-
For tutorials and further information visit [projectf.io](https://projectf.io).
7+
For tutorials and further information, visit [projectf.io](https://projectf.io).
8+
9+
_NB. Pixel coordinates are now signed values and have been renamed; see module documentation for [display timings](doc/modules.md#display-timings)._
810

911
## Contents
1012

@@ -33,6 +35,8 @@ Direct TMDS generation on FPGA requires high-frequency clocks (742.5 MHz for 720
3335

3436
The Black Mesa Labs (BML) Pmod is based on the Texas Instruments [TFP410](http://www.ti.com/product/TFP410). The Pmod is restricted to standard DVI features but allows even tiny FPGAs to support DVI signalling. As of February 2019, there is an unresolved issue when using a BML DVI Pmod with HDMI displays: some displays report a signal error and show nothing. This issue isn't confined to Project F but has also been reported by Black Mesa Labs themselves.
3537

38+
![](doc/display-pmods.jpg?raw=true "")
39+
3640

3741
## Display Resolution Support
3842
The following four display resolutions are tested and included by default (all at 60 Hz refresh rate):
@@ -86,7 +90,7 @@ Details on module interfaces can be found in the [modules](doc/modules.md) doc.
8690

8791
## Testing
8892

89-
If it isn't tested, it doesn't work. Project F tests its designs in simulation and on real hardware. For the display controller you can use the included [test benches](hdl/test) and [Python TMDS model](#tmds-encoder-model) to exercise the design.
93+
If it isn't tested, it doesn't work. Project F tests its designs in simulation and on real hardware. For the display controller, you can use the included [test benches](hdl/test) and [Python TMDS model](#tmds-encoder-model) to exercise the design.
9094

9195
We haven't formally verified the design yet, but plan to do this for the display timings and TMDS encoder during 2019. If you're interested in learning more about formal verification, check out Clifford Wolf's [Formal Verification with SymbiYosys and Yosys-SMTBMC](http://www.clifford.at/papers/2017/smtbmc-sby/).
9296

@@ -124,12 +128,12 @@ The following table shows utilization of the display-controller with the gradien
124128
Interface LUT FF
125129
-----------------------------
126130
DVI on FPGA 278 86
127-
DVI BML 3-bit 49 32
131+
DVI BML 3-bit 86 32
128132
DVI BML 24-bit TBC TBC
129-
VGA 12-bit 67 32
133+
VGA 12-bit 92 32
130134
-----------------------------
131135
Synthesized and implemented with Vivado 2019.1 using default options.
132136

133-
For comparison an Artix A35T has 20,800 LUT6 and 41,600 FF, while the tiny Spartan 7S6 has 3,752 LUT6 and 7,500 FF.
137+
For comparison, an Artix A35T has 20,800 LUT6 and 41,600 FF, while the tiny Spartan 7S6 has 3,752 LUT6 and 7,500 FF.
134138

135139
NB. If you drive the "DVI on FPGA" display controller with a few fixed colours, such as the simple test bench, the optimizer removes a significant part of the design, resulting in misleadingly low utilization.

doc/display-pmods.jpg

85.8 KB
Loading

doc/display-timings.jpg

75.2 KB
Loading

doc/modules.md

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,11 +77,11 @@ The [demo](/hdl/demo) modules include appropriate parameters for four standard p
7777

7878

7979
## Display Timings
80-
The display timings generator turns timing parameters into appropriately timed sync pulses and provides the current screen co-ordinates. Accurate timings depend on an accurate [pixel clock](#display-clocks). ([display_timings.v](/hdl/display_timings.v))
80+
The display timings generator turns timing parameters into appropriately timed sync pulses and provides the current screen coordinates. Accurate timings depend on an accurate [pixel clock](#display-clocks). ([display_timings.v](/hdl/display_timings.v))
8181

8282
### Inputs
8383

84-
* `i_pixclk` - pixel clock
84+
* `i_pix_clk` - pixel clock
8585
* `i_rst` - reset (active high)
8686

8787
The pixel clock must be suitable for the timings given in the parameters (see display clocks, above).
@@ -92,18 +92,16 @@ The pixel clock must be suitable for the timings given in the parameters (see di
9292
* `o_vs` - vertical sync
9393
* `o_de` - display enable: high during active video
9494
* `o_frame` - high for one tick at the start of each frame
95-
* `o_h [15:0]` - horizontal beam position (including blanking)
96-
* `o_v [15:0]` - vertical beam position (including blanking)
97-
* `o_x [15:0]` - horizontal screen position (active pixels)
98-
* `o_y [15:0]` - vertical screen position (active pixels)
95+
* `o_sx [15:0]` - horizontal screen position (signed)
96+
* `o_sy [15:0]` - vertical screen position (signed)
9997

100-
The positional outputs `(h,v)` and `(x,y)` allow you to determine the current pixel AKA "beam position". The values provided by `h` & `v `include the blanking interval, while `x` & `y` only include valid on-screen positions. For simple drawing or bitmap display you can use `(x,y)` and safely ignore `(h,v)`. However, if you're doing calculations in real time "racing the beam", then you'll want to perform actions in the blanking interval, which is where (h,v) comes in.
98+
The current beam position is given by `(o_sx,o_sy)`. `o_sx` and `o_sy` are **signed** 16-bit values.
10199

102-
Project F considers blanking intervals to occur _before_ active pixels. At the start of a frame (indicated by the `o_frame` signal), you have the blanking intervals in which to work before active pixel drawing occurs. The following sketch this for 1280x720p60 (other resolutions work in the same way):
100+
When display enable (`o_de`) is high, these values provide the active drawing pixel and are always positive. During the blanking interval, one or both of `o_sx` and `o_sy` will be negative. This allows you to prepare for drawing, e.g. if you have a two cycle latency to retrieve a pixel's colour you can request the data for the first pixel of a line when `o_sx == -2`.
103101

104-
![](display-controller-hv-xy.jpg?raw=true "")
102+
![](display-timings.jpg?raw=true "")
105103

106-
NB. `x` and `y` are 0 during the blanking interval.
104+
At the start of a 1280x720p60 frame, `o_sx == -370` and `o_sy == -45`. Active drawing starts at `o_sx == 0` and `o_sy == 0` and the final coordinates are `o_sx == 1279` and `o_sy == 719`.
107105

108106
Horizontal and vertical sync may be active high or low depending on the display mode; this is controlled using the `H_POL` and `V_POL` parameters (below).
109107

hdl/demo/display_demo_dvi.v

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ module display_demo_dvi(
5151
);
5252

5353
// Display Timings
54-
wire [15:0] x; // horizontal screen position
55-
wire [15:0] y; // vertical screen position
54+
wire [15:0] sx; // horizontal screen position
55+
wire [15:0] sy; // vertical screen position
5656
wire h_sync; // horizontal sync
5757
wire v_sync; // vertical sync
5858
wire de; // display enable
@@ -71,16 +71,14 @@ module display_demo_dvi(
7171
.V_POL(1) // 0 1 1 1
7272
)
7373
display_timings_inst (
74-
.i_pixclk(pix_clk),
74+
.i_pix_clk(pix_clk),
7575
.i_rst(rst),
7676
.o_hs(h_sync),
7777
.o_vs(v_sync),
7878
.o_de(de),
7979
.o_frame(frame),
80-
.o_h(),
81-
.o_v(),
82-
.o_x(x),
83-
.o_y(y)
80+
.o_sx(sx),
81+
.o_sy(sy)
8482
);
8583

8684
// test card colour output
@@ -92,7 +90,7 @@ module display_demo_dvi(
9290
test_card_simple #(
9391
.H_RES(1280) // horizontal resolution
9492
) test_card_inst (
95-
.i_x(x),
93+
.i_x(sx),
9694
.o_red(red),
9795
.o_green(green),
9896
.o_blue(blue)
@@ -104,8 +102,8 @@ module display_demo_dvi(
104102
// .V_RES(720) // vertical resolution
105103
// )
106104
// test_card_inst (
107-
// .i_x(x),
108-
// .i_y(y),
105+
// .i_x(sx),
106+
// .i_y(sy),
109107
// .o_red(red),
110108
// .o_green(green),
111109
// .o_blue(blue)
@@ -114,8 +112,8 @@ module display_demo_dvi(
114112
// // Test Card: Gradient - ENABLE ONE TEST CARD INSTANCE ONLY
115113
// localparam GRAD_STEP = 2; // step right shift: 480=2, 720=2, 1080=3
116114
// test_card_gradient test_card_inst (
117-
// .i_y(y[GRAD_STEP+7:GRAD_STEP]),
118-
// .i_x(x[5:0]),
115+
// .i_y(sy[GRAD_STEP+7:GRAD_STEP]),
116+
// .i_x(sx[5:0]),
119117
// .o_red(red),
120118
// .o_green(green),
121119
// .o_blue(blue)

hdl/demo/display_demo_dvi_pmod3.v

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,8 @@ module display_demo_dvi_pmod3(
4646
);
4747

4848
// Display Timings
49-
wire [15:0] x; // horizontal pixel position
50-
wire [15:0] y; // vertical pixel position
49+
wire [15:0] sx; // horizontal pixel position
50+
wire [15:0] sy; // vertical pixel position
5151
wire h_sync; // horizontal sync
5252
wire v_sync; // vertical sync
5353
wire de; // display enable
@@ -66,16 +66,14 @@ module display_demo_dvi_pmod3(
6666
.V_POL(0) // 0 1 1 1
6767
)
6868
display_timings_inst (
69-
.i_pixclk(pix_clk),
69+
.i_pix_clk(pix_clk),
7070
.i_rst(rst),
7171
.o_hs(h_sync),
7272
.o_vs(v_sync),
7373
.o_de(de),
7474
.o_frame(frame),
75-
.o_h(),
76-
.o_v(),
77-
.o_x(x),
78-
.o_y(y)
75+
.o_sx(sx),
76+
.o_sy(sy)
7977
);
8078

8179
// test card colour output
@@ -87,7 +85,7 @@ module display_demo_dvi_pmod3(
8785
test_card_simple #(
8886
.H_RES(640) // horizontal resolution
8987
) test_card_inst (
90-
.i_x(x),
88+
.i_x(sx),
9189
.o_red(red),
9290
.o_green(green),
9391
.o_blue(blue)
@@ -99,8 +97,8 @@ module display_demo_dvi_pmod3(
9997
// .V_RES(480) // vertical resolution
10098
// )
10199
// test_card_inst (
102-
// .i_x(x),
103-
// .i_y(y),
100+
// .i_x(sx),
101+
// .i_y(sy),
104102
// .o_red(red),
105103
// .o_green(green),
106104
// .o_blue(blue)
@@ -109,8 +107,8 @@ module display_demo_dvi_pmod3(
109107
// // Test Card: Gradient - ENABLE ONE TEST CARD INSTANCE ONLY
110108
// localparam GRAD_STEP = 2; // step right shift: 480=2, 720=2, 1080=3
111109
// test_card_gradient test_card_inst (
112-
// .i_x(x[5:0]),
113-
// .i_y(y[GRAD_STEP+7:GRAD_STEP]),
110+
// .i_x(sx[5:0]),
111+
// .i_y(sy[GRAD_STEP+7:GRAD_STEP]),
114112
// .o_red(red),
115113
// .o_green(green),
116114
// .o_blue(blue)

hdl/demo/display_demo_vga.v

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ module display_demo_vga(
4444
);
4545

4646
// Display Timings
47-
wire [15:0] x; // horizontal pixel position
48-
wire [15:0] y; // vertical pixel position
47+
wire [15:0] sx; // horizontal pixel position
48+
wire [15:0] sy; // vertical pixel position
4949
wire h_sync; // horizontal sync
5050
wire v_sync; // vertical sync
5151
wire de; // display enable
@@ -64,16 +64,14 @@ module display_demo_vga(
6464
.V_POL(0) // 0 1 1 1
6565
)
6666
display_timings_inst (
67-
.i_pixclk(pix_clk),
67+
.i_pix_clk(pix_clk),
6868
.i_rst(rst),
6969
.o_hs(h_sync),
7070
.o_vs(v_sync),
7171
.o_de(de),
7272
.o_frame(frame),
73-
.o_h(),
74-
.o_v(),
75-
.o_x(x),
76-
.o_y(y)
73+
.o_sx(sx),
74+
.o_sy(sy)
7775
);
7876

7977
// test card colour output
@@ -85,7 +83,7 @@ module display_demo_vga(
8583
test_card_simple #(
8684
.H_RES(640) // horizontal resolution
8785
) test_card_inst (
88-
.i_x(x),
86+
.i_x(sx),
8987
.o_red(red),
9088
.o_green(green),
9189
.o_blue(blue)
@@ -97,8 +95,8 @@ module display_demo_vga(
9795
// .V_RES(480) // vertical resolution
9896
// )
9997
// test_card_inst (
100-
// .i_x(x),
101-
// .i_y(y),
98+
// .i_x(sx),
99+
// .i_y(sy),
102100
// .o_red(red),
103101
// .o_green(green),
104102
// .o_blue(blue)
@@ -107,8 +105,8 @@ module display_demo_vga(
107105
// // Test Card: Gradient - ENABLE ONE TEST CARD INSTANCE ONLY
108106
// localparam GRAD_STEP = 2; // step right shift: 480=2, 720=2, 1080=3
109107
// test_card_gradient test_card_inst (
110-
// .i_x(x[5:0]),
111-
// .i_y(y[GRAD_STEP+7:GRAD_STEP]),
108+
// .i_x(sx[5:0]),
109+
// .i_y(sy[GRAD_STEP+7:GRAD_STEP]),
112110
// .o_red(red),
113111
// .o_green(green),
114112
// .o_blue(blue)

hdl/display_timings.v

Lines changed: 33 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -20,70 +20,61 @@ module display_timings #(
2020
V_POL=0 // vertical sync polarity (0:neg, 1:pos)
2121
)
2222
(
23-
input wire i_pixclk, // pixel clock
24-
input wire i_rst, // reset: restarts frame (active high)
25-
output wire o_hs, // horizontal sync
26-
output wire o_vs, // vertical sync
27-
output wire o_de, // display enable: high during active video
28-
output wire o_frame, // high for one tick at the start of each frame
29-
output reg [15:0] o_h, // horizontal beam position (including blanking)
30-
output reg [15:0] o_v, // vertical beam position (including blanking)
31-
output wire [15:0] o_x, // horizontal screen position (active pixels)
32-
output wire [15:0] o_y // vertical screen position (active pixels)
23+
input wire i_pix_clk, // pixel clock
24+
input wire i_rst, // reset: restarts frame (active high)
25+
output wire o_hs, // horizontal sync
26+
output wire o_vs, // vertical sync
27+
output wire o_de, // display enable: high during active video
28+
output wire o_frame, // high for one tick at the start of each frame
29+
output reg signed [15:0] o_sx, // horizontal beam position (including blanking)
30+
output reg signed [15:0] o_sy // vertical beam position (including blanking)
3331
);
3432

3533
// Horizontal: sync, active, and pixels
36-
localparam HS_STA = H_FP - 1; // sync start (first pixel is 0)
37-
localparam HS_END = HS_STA + H_SYNC; // sync end
38-
localparam HA_STA = HS_END + H_BP; // active start
39-
localparam HA_END = HA_STA + H_RES; // active end
40-
localparam LINE = HA_END; // line pixels
34+
localparam signed H_STA = 0 - H_FP - H_SYNC - H_BP; // horizontal start
35+
localparam signed HS_STA = H_STA + H_FP; // sync start
36+
localparam signed HS_END = HS_STA + H_SYNC; // sync end
37+
localparam signed HA_STA = 0; // active start = 0
38+
localparam signed HA_END = H_RES - 1; // active end
4139

4240
// Vertical: sync, active, and pixels
43-
localparam VS_STA = V_FP - 1; // sync start (first line is 0)
44-
localparam VS_END = VS_STA + V_SYNC; // sync end
45-
localparam VA_STA = VS_END + V_BP; // active start
46-
localparam VA_END = VA_STA + V_RES; // active end
47-
localparam FRAME = VA_END; // frame lines
41+
localparam signed V_STA = 0 - V_FP - V_SYNC - V_BP; // vertical start
42+
localparam signed VS_STA = V_STA + V_FP; // sync start
43+
localparam signed VS_END = VS_STA + V_SYNC; // sync end
44+
localparam signed VA_STA = 0; // active start
45+
localparam signed VA_END = V_RES - 1; // active end
4846

4947
// generate sync signals with correct polarity
50-
assign o_hs = H_POL ? (o_h > HS_STA && o_h <= HS_END)
51-
: ~(o_h > HS_STA && o_h <= HS_END);
52-
assign o_vs = V_POL ? (o_v > VS_STA && o_v <= VS_END)
53-
: ~(o_v > VS_STA && o_v <= VS_END);
48+
assign o_hs = H_POL ? (o_sx > HS_STA && o_sx <= HS_END)
49+
: ~(o_sx > HS_STA && o_sx <= HS_END);
50+
assign o_vs = V_POL ? (o_sy > VS_STA && o_sy <= VS_END)
51+
: ~(o_sy > VS_STA && o_sy <= VS_END);
5452

5553
// display enable: high during active period
56-
assign o_de = o_h > HA_STA && o_h <= HA_END
57-
&& o_v > VA_STA && o_v <= VA_END;
58-
59-
// keep o_x and o_y bound within active pixels
60-
assign o_x = (o_de && o_h > HA_STA && o_h <= HA_END) ?
61-
o_h - (HA_STA + 1): 0;
62-
assign o_y = (o_de && o_v > VA_STA && o_v <= VA_END) ?
63-
o_v - (VA_STA + 1): 0;
54+
assign o_de = o_sx >= 0 && o_sy >= 0;
6455

6556
// o_frame: high for one tick at the start of each frame
66-
assign o_frame = (o_v == 0 && o_h == 0);
57+
assign o_frame = (o_sy == V_STA && o_sx == H_STA);
6758

68-
always @ (posedge i_pixclk)
59+
always @ (posedge i_pix_clk)
6960
begin
7061
if (i_rst) // reset to start of frame
7162
begin
72-
o_h <= 0;
73-
o_v <= 0;
63+
o_sx <= H_STA;
64+
o_sy <= V_STA;
7465
end
7566
else
7667
begin
77-
if (o_h == LINE) // end of line
68+
if (o_sx == HA_END) // end of line
7869
begin
79-
o_h <= 0;
80-
if (o_v == FRAME) // end of frame
81-
o_v <= 0;
70+
o_sx <= H_STA;
71+
if (o_sy == VA_END) // end of frame
72+
o_sy <= V_STA;
8273
else
83-
o_v <= o_v + 1;
74+
o_sy <= o_sy + 16'sh1;
8475
end
8576
else
86-
o_h <= o_h + 1;
77+
o_sx <= o_sx + 16'sh1;
8778
end
8879
end
8980
endmodule

hdl/serializer_10to1.v

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module serializer_10to1(
1717
reg rst_oserdes; // oserdes reset (active high)
1818
(* ASYNC_REG = "TRUE" *) reg [1:0] rst_shf; // reset shift reg
1919

20-
initial rst_oserdes = 1'b1; // start of with reset asserted
20+
initial rst_oserdes = 1'b1; // start off with reset asserted
2121
initial rst_shf = 2'b11; // and reset shift reg populated
2222

2323
always @(posedge i_clk or posedge i_rst)

0 commit comments

Comments
 (0)