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
|
library ieee;
library work;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.numeric_std.all;
use work.ppu_consts.all;
entity ppu_sprite_bg is port(
-- inputs
CLK : in std_logic; -- pipeline clock
RESET : in std_logic; -- reset clock counter
OE : in std_logic; -- output enable (of CIDX)
X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x
Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0); -- current screen pixel y
-- aux inputs
BG_SHIFT_X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0);
BG_SHIFT_Y : in std_logic_vector(PPU_POS_V_WIDTH-1 downto 0);
-- used memory blocks
BAM_ADDR : out std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0);
BAM_DATA : in std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
TMM_ADDR : out std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0);
TMM_DATA : in std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
-- outputs
CIDX : out std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0)); -- output color
end ppu_sprite_bg;
architecture Behavioral of ppu_sprite_bg is
component ppu_sprite_transform port(
XI : in std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- pixel position relative to tile
YI : in std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0); -- pixel position relative to tile
FLIP_H, FLIP_V : in std_logic; -- flip sprite
XO : out std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0); -- new pixel position relative to tile
YO : out std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0)); -- new pixel position relative to tile
end component;
signal O_BAM_ADDR : std_logic_vector(PPU_BAM_ADDR_WIDTH-1 downto 0) := (others => '0');
signal I_BAM_DATA : std_logic_vector(PPU_BAM_DATA_WIDTH-1 downto 0);
signal O_TMM_ADDR : std_logic_vector(PPU_TMM_ADDR_WIDTH-1 downto 0) := (others => '0');
signal I_TMM_DATA : std_logic_vector(PPU_TMM_DATA_WIDTH-1 downto 0);
signal FLIP_H, FLIP_V : std_logic := '0';
signal TMM_IDX : std_logic_vector(PPU_TILE_INDEX_WIDTH-1 downto 0) := (others => '0');
alias BAM_DATA_COL_IDX is I_BAM_DATA(12 downto 10);
signal TMM_DATA_PAL_IDX : std_logic_vector(PPU_PALETTE_COLOR_WIDTH-1 downto 0);
signal O_CIDX : std_logic_vector(PPU_PALETTE_CIDX_WIDTH-1 downto 0) := (others => '0');
type states is (PL_BAM_ADDR, PL_BAM_DATA, PL_TMM_ADDR, PL_TMM_DATA);
signal state, next_state : states := PL_BAM_ADDR;
-- docs/architecture.md#background-attribute-memory
alias BAM_DATA_FLIP_H is I_BAM_DATA(14);
alias BAM_DATA_FLIP_V is I_BAM_DATA(13);
alias BAM_DATA_TILE_IDX is I_BAM_DATA(9 downto 0);
signal PIXEL_ABS_X, PIXEL_ABS_Y : integer := 0; -- absolute pixel position (relative to BG canvas instead of viewport)
signal TILE_IDX_X, TILE_IDX_Y : integer := 0; -- background canvas tile grid xy
signal TILE_PIXEL_IDX_X, TILE_PIXEL_IDX_Y : integer := 0; -- xy position of pixel within tile
signal TILE_PIXEL_IDX : integer := 0; -- index of pixel within tile
signal TRANS_TILE_PIDX_X, TRANS_TILE_PIDX_Y : integer := 0; -- transformed xy position of pixel within tile
signal TILEMAP_WORD_OFFSET : integer := 0;
signal TRANSFORM_XI, TRANSFORM_XO : std_logic_vector(PPU_SPRITE_POS_H_WIDTH-1 downto 0);
signal TRANSFORM_YI, TRANSFORM_YO : std_logic_vector(PPU_SPRITE_POS_V_WIDTH-1 downto 0);
signal PIXEL_BIT_OFFSET : integer := 0;
begin
-- TODO: fix latches
-- CIDX tri-state driver
CIDX <= O_CIDX when OE = '1' else (others => 'Z');
-- internal line separators
FLIP_H <= BAM_DATA_FLIP_H;
FLIP_V <= BAM_DATA_FLIP_V;
O_CIDX <= BAM_DATA_COL_IDX & TMM_DATA_PAL_IDX;
-- BAM ADDR
PIXEL_ABS_X <= to_integer(unsigned(X)) + to_integer(unsigned(BG_SHIFT_X));
PIXEL_ABS_Y <= to_integer(unsigned(Y)) + to_integer(unsigned(BG_SHIFT_Y));
TILE_IDX_X <= PIXEL_ABS_X / 16;
TILE_IDX_Y <= PIXEL_ABS_Y / 16;
TILE_PIXEL_IDX_X <= PIXEL_ABS_X - TILE_IDX_X * 16;
TILE_PIXEL_IDX_Y <= PIXEL_ABS_Y - TILE_IDX_Y * 16;
O_BAM_ADDR <= std_logic_vector(to_unsigned((TILE_IDX_Y * integer(PPU_BG_CANVAS_TILES_H)) + TILE_IDX_X, PPU_BAM_ADDR_WIDTH));
-- BAM DATA + FAM ADDR
TRANSFORM_XI <= std_logic_vector(to_unsigned(TILE_PIXEL_IDX_X, PPU_SPRITE_POS_H_WIDTH));
TRANSFORM_YI <= std_logic_vector(to_unsigned(TILE_PIXEL_IDX_Y, PPU_SPRITE_POS_V_WIDTH));
transform: component ppu_sprite_transform port map(
XI => TRANSFORM_XI,
YI => TRANSFORM_YI,
FLIP_H => FLIP_H,
FLIP_V => FLIP_V,
XO => TRANSFORM_XO,
YO => TRANSFORM_YO);
TRANS_TILE_PIDX_X <= to_integer(unsigned(TRANSFORM_XO));
TRANS_TILE_PIDX_Y <= to_integer(unsigned(TRANSFORM_YO));
TILE_PIXEL_IDX <= integer(PPU_SPRITE_WIDTH) * TRANS_TILE_PIDX_Y + TRANS_TILE_PIDX_X;
TILEMAP_WORD_OFFSET <= TILE_PIXEL_IDX / PPU_PIXELS_PER_TILE_WORD;
PIXEL_BIT_OFFSET <= TILE_PIXEL_IDX mod PPU_PIXELS_PER_TILE_WORD;
O_TMM_ADDR <= std_logic_vector(to_unsigned(PPU_SPRITE_PIXELS_PER_WORD * to_integer(unsigned(BAM_DATA_TILE_IDX)) + TILEMAP_WORD_OFFSET, PPU_TMM_ADDR_WIDTH));
-- TMM DATA
with PIXEL_BIT_OFFSET select
TMM_DATA_PAL_IDX <= I_TMM_DATA(2 downto 0) when 0,
I_TMM_DATA(5 downto 3) when 1,
I_TMM_DATA(8 downto 6) when 2,
I_TMM_DATA(11 downto 9) when 3,
I_TMM_DATA(14 downto 12) when 4,
(others => '0') when others;
-- state machine (pipeline stage counter)
fsm: process(CLK, RESET)
begin
if RESET = '1' then
-- reset state
state <= PL_BAM_ADDR;
-- reset internal pipeline registers
-- O_BAM_ADDR <= (others => '0');
-- I_BAM_DATA <= (others => '0');
-- O_TMM_ADDR <= (others => '0');
-- I_TMM_DATA <= (others => '0');
-- reset working color register
-- O_CIDX <= (others => '0');
elsif rising_edge(CLK) then
state <= next_state;
end if;
end process;
-- sync read/write
process(state)
begin
next_state <= state;
case state is
when PL_BAM_ADDR =>
next_state <= PL_BAM_DATA;
BAM_ADDR <= O_BAM_ADDR;
when PL_BAM_DATA =>
next_state <= PL_TMM_ADDR;
I_BAM_DATA <= BAM_DATA;
when PL_TMM_ADDR =>
next_state <= PL_TMM_DATA;
TMM_ADDR <= O_TMM_ADDR;
when PL_TMM_DATA =>
next_state <= PL_BAM_ADDR;
I_TMM_DATA <= TMM_DATA;
end case;
end process;
end Behavioral;
|