/
top_space_f.sv
170 lines (151 loc) · 5 KB
/
top_space_f.sv
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Project F: Ad Astra - Top Space 'F' (Arty Pmod VGA)
// (C)2022 Will Green, open source hardware released under the MIT License
// Learn more at https://projectf.io/posts/fpga-ad-astra/
`default_nettype none
`timescale 1ns / 1ps
module top_space_f (
input wire logic clk_100m, // 100 MHz clock
input wire logic btn_rst_n, // reset button (active low)
output logic vga_hsync, // horizontal sync
output logic vga_vsync, // vertical sync
output logic [3:0] vga_r, // 4-bit VGA red
output logic [3:0] vga_g, // 4-bit VGA green
output logic [3:0] vga_b // 4-bit VGA blue
);
// generate pixel clock
logic clk_pix;
logic clk_pix_locked;
logic rst_pix;
clock_480p clock_pix_inst (
.clk_100m,
.rst(!btn_rst_n), // reset button is active low
.clk_pix,
/* verilator lint_off PINCONNECTEMPTY */
.clk_pix_5x(), // not used for VGA output
/* verilator lint_on PINCONNECTEMPTY */
.clk_pix_locked
);
always_ff @(posedge clk_pix) rst_pix <= !clk_pix_locked; // wait for clock lock
// display sync signals and coordinates
localparam CORDW = 16;
logic signed [CORDW-1:0] sx, sy;
logic hsync, vsync;
logic de, line;
display_480p #(.CORDW(CORDW)) display_inst (
.clk_pix,
.rst_pix,
.sx,
.sy,
.hsync,
.vsync,
.de,
/* verilator lint_off PINCONNECTEMPTY */
.frame(),
/* verilator lint_on PINCONNECTEMPTY */
.line
);
// font glyph ROM
localparam FONT_WIDTH = 8; // width in pixels (also ROM width)
localparam FONT_HEIGHT = 8; // height in pixels
localparam FONT_GLYPHS = 64; // number of glyphs
localparam F_ROM_DEPTH = FONT_GLYPHS * FONT_HEIGHT;
localparam FONT_FILE = "font_unscii_8x8_latin_uc.mem";
logic [$clog2(F_ROM_DEPTH)-1:0] font_rom_addr;
logic [FONT_WIDTH-1:0] font_rom_data; // line of glyph pixels
rom_sync #(
.WIDTH(FONT_WIDTH),
.DEPTH(F_ROM_DEPTH),
.INIT_F(FONT_FILE)
) font_rom (
.clk(clk_pix),
.addr(font_rom_addr),
.data(font_rom_data)
);
// sprite
localparam SPR_SCALE_X = 32; // enlarge sprite width by this factor
localparam SPR_SCALE_Y = 32; // enlarge sprite height by this factor
// horizontal and vertical screen position of letter
localparam SPR_X = 192;
localparam SPR_Y = 112;
// signal to start sprite drawing
logic spr_start;
always_comb spr_start = (line && sy == SPR_Y);
// subtract 0x20 from code points as font starts at U+0020
localparam SPR_GLYPH_ADDR = FONT_HEIGHT * 'h26; // F U+0046
// font ROM address
logic [$clog2(FONT_HEIGHT)-1:0] spr_glyph_line;
logic spr_fdma; // font ROM DMA slot
always_comb begin
font_rom_addr = 0;
spr_fdma = line; // load glyph line at start of horizontal blanking
/* verilator lint_off WIDTH */
font_rom_addr = (spr_fdma) ? SPR_GLYPH_ADDR + spr_glyph_line : 0;
/* verilator lint_on WIDTH */
end
logic spr_pix; // sprite pixel
sprite #(
.WIDTH(FONT_WIDTH),
.HEIGHT(FONT_HEIGHT),
.SCALE_X(SPR_SCALE_X),
.SCALE_Y(SPR_SCALE_Y),
.LSB(0),
.CORDW(CORDW),
.ADDRW($clog2(FONT_HEIGHT))
) spr (
.clk(clk_pix),
.rst(rst_pix),
.start(spr_start),
.dma_avail(spr_fdma),
.sx,
.sprx(SPR_X),
.data_in(font_rom_data),
.pos(spr_glyph_line),
.pix(spr_pix),
/* verilator lint_off PINCONNECTEMPTY */
.drawing(),
.done()
/* verilator lint_on PINCONNECTEMPTY */
);
// starfields
logic sf1_on, sf2_on, sf3_on;
/* verilator lint_off UNUSED */
logic [7:0] sf1_star, sf2_star, sf3_star;
/* verilator lint_on UNUSED */
starfield #(.INC(-1), .SEED(21'h9A9A9)) sf1 (
.clk(clk_pix),
.en(1'b1),
.rst(rst_pix),
.sf_on(sf1_on),
.sf_star(sf1_star)
);
starfield #(.INC(-2), .SEED(21'hA9A9A)) sf2 (
.clk(clk_pix),
.en(1'b1),
.rst(rst_pix),
.sf_on(sf2_on),
.sf_star(sf2_star)
);
starfield #(.INC(-4), .MASK(21'h7FF)) sf3 (
.clk(clk_pix),
.en(1'b1),
.rst(rst_pix),
.sf_on(sf3_on),
.sf_star(sf3_star)
);
// sprite colour & star brightness
logic [3:0] red_spr, green_spr, blue_spr, starlight;
always_comb begin
{red_spr, green_spr, blue_spr} = (spr_pix) ? 12'hFC0 : 12'h000;
starlight = (sf1_on) ? sf1_star[7:4] :
(sf2_on) ? sf2_star[7:4] :
(sf3_on) ? sf3_star[7:4] : 4'h0;
end
// VGA output
always_ff @(posedge clk_pix) begin
vga_hsync <= hsync;
vga_vsync <= vsync;
vga_r <= de ? spr_pix ? red_spr : starlight : 4'h0;
vga_g <= de ? spr_pix ? green_spr : starlight : 4'h0;
vga_b <= de ? spr_pix ? blue_spr : starlight : 4'h0;
end
endmodule