├── .project
├── APU
├── APU_Main.v
├── ac97wav.py
├── apu.py
├── apu_convert.py
├── apu_tb.py
├── clk_util.py
├── cpu_bus.py
├── nsf.py
└── smb.nsf
├── CartridgeROM.vhd
├── CartridgeROM_Coregen.vhd
├── ColorPalette.vhd
├── Coregen
├── chr_rom_NEStress.ngc
├── chr_rom_smb.ngc
├── coregen.cgc
├── coregen.cgp
├── prg_rom_NESStress.ngc
└── prg_rom_smb.ngc
├── Genesys_NES.vhd
├── HDMI
├── HDMIController.vhd
├── iic_init.v
└── tft_interface.v
├── NES_2A03
├── ClockDivider.vhd
├── DanPack.vhd
├── Dan_2A03.vhd
├── SRAM.vhd
├── T65.vhd
├── T65_ALU.vhd
├── T65_MCode.vhd
└── T65_Pack.vhd
├── NES_Mainboard.vhd
├── NES_Pack.vhd
├── PPU
├── Loopy_Scrolling.vhd
├── PPU.vhd
├── PPU_Pack.vhd
├── SpriteSelector.vhd
└── TileFetcher.vhd
├── README.md
├── TestBenches
├── DummySound_TB.vhd
├── NES_Framebuffer_TB.vhd
├── PPU_tb.vhd
├── SpriteSelector_TB.vhd
└── nes_top_tb.vhd
├── ac97
├── Talkthrough_Parts.vhd
└── ac97_top.vhd
├── doc
├── implementation-issues.txt
├── myhdl-findings.txt
├── teaching.doc
├── todo.txt
└── tutorial-genesys.txt
├── nes_top.ucf
├── roms
├── fpgasmb.nes
├── smb.nes
├── smb_chr.coe
├── smb_prg.coe
├── stress.nes
├── stress_chr.coe
└── stress_prg.coe
├── synaesthesia
├── genesys.xst
├── run_xst.sh
├── synthesis.py
├── vhd2ngc
└── xilinx.py
├── thesis.pdf
└── tools
├── cartridge.py
├── fbview
├── SDLMain.h
├── SDLMain.m
├── build.sh
└── fbview.cpp
├── file_rom.py
└── romconv.py
/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | nesfpga
4 |
5 |
6 |
7 |
8 |
9 | org.eclipse.xtext.ui.shared.xtextBuilder
10 |
11 |
12 |
13 |
14 |
15 | com.sigasi.hdt.vhdl.ui.vhdlNature
16 | org.eclipse.xtext.ui.shared.xtextNature
17 |
18 |
19 |
20 | Common Libraries
21 | 2
22 | virtual:/virtual
23 |
24 |
25 | Common Libraries/IEEE
26 | 2
27 | sigasiresource:/vhdl/93/IEEE
28 |
29 |
30 | Common Libraries/IEEE Synopsys
31 | 2
32 | sigasiresource:/vhdl/93/IEEE%20Synopsys
33 |
34 |
35 | Common Libraries/STD
36 | 2
37 | sigasiresource:/vhdl/93/STD
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/APU/ac97wav.py:
--------------------------------------------------------------------------------
1 | from myhdl import *
2 |
3 | from clk_util import *
4 |
5 | def AC97_WavWriter(PCM, filename):
6 | import wave
7 | outfile = wave.open(filename, 'w')
8 | outfile.setnchannels(1)
9 | outfile.setframerate(48000)
10 | outfile.setsampwidth(1)
11 |
12 | CLK = Signal()
13 |
14 | AC97_CLK_period = 20833
15 |
16 | ac97_clk_gen = CLK_Gen(CLK, AC97_CLK_period)
17 |
18 | @always(CLK.posedge)
19 | def writeSample():
20 | # Pack as signed byte
21 | import struct
22 | outfile.writeframes(struct.pack('b', PCM - 127))
23 | #outfile.writeframes(chr(int(PCM - 127) * 4 + ))
24 |
25 | return ac97_clk_gen, writeSample
26 |
27 |
--------------------------------------------------------------------------------
/APU/apu.py:
--------------------------------------------------------------------------------
1 | from myhdl import *
2 |
3 | NES_CLK_period = 46 * 12
4 | # constant AC97_CLK_period : time := 20.833333333333332 us; -- 48 kHz
5 | # constant CLK_period : time := 46.560848137510206 ns; -- 21.477272 MhZ
6 |
7 | def APU_Main(
8 | CLK,
9 | RSTN,
10 | PHI2_CE,
11 | RW10,
12 |
13 | Address,
14 | Data_read,
15 | Data_write,
16 |
17 | Interrupt,
18 | PCM_out
19 | ):
20 |
21 | APU_CE = Signal(False)
22 | APU_CE_cnt = Signal(False)
23 |
24 | Pulse1_CS = Signal(False)
25 | Pulse2_CS = Signal(False)
26 | Noise_CS = Signal(False)
27 | Triangle_CS = Signal(False)
28 |
29 | HalfFrame_CE = Signal(False)
30 | QuarterFrame_CE = Signal(False)
31 |
32 | PCM_pulse1 = Signal(intbv()[4:0])
33 | PCM_pulse2 = Signal(intbv()[4:0])
34 | PCM_noise = Signal(intbv()[4:0])
35 | PCM_triangle = Signal(intbv()[4:0])
36 |
37 | frameCounter = APU_FrameCounter(CLK, PHI2_CE, APU_CE, RW10, Address, Data_write, HalfFrame_CE, QuarterFrame_CE, Interrupt)
38 | pulse1 = APU_Pulse(CLK, RSTN, PHI2_CE, APU_CE, RW10, Address, Data_write, Pulse1_CS, HalfFrame_CE, QuarterFrame_CE, PCM_pulse1)
39 | pulse2 = APU_Pulse(CLK, RSTN, PHI2_CE, APU_CE, RW10, Address, Data_write, Pulse2_CS, HalfFrame_CE, QuarterFrame_CE, PCM_pulse2)
40 | noise = APU_Noise(CLK, RSTN, PHI2_CE, APU_CE, RW10, Address, Data_write, Noise_CS, HalfFrame_CE, QuarterFrame_CE, PCM_noise)
41 | triangle = APU_Triangle(CLK, RSTN, PHI2_CE, APU_CE, RW10, Address, Data_write, Triangle_CS, HalfFrame_CE, QuarterFrame_CE, PCM_triangle)
42 |
43 | @always(CLK.posedge)
44 | def ce():
45 | if PHI2_CE:
46 | APU_CE_cnt.next = not APU_CE_cnt
47 | # print " CE", APU_CE
48 | # else:
49 | # print "NOT CE", APU_CE
50 |
51 | @always_comb
52 | def chipselect():
53 | Pulse1_CS.next = 0x4000 <= Address and Address < 0x4004
54 | Pulse2_CS.next = 0x4004 <= Address and Address < 0x4008
55 | Triangle_CS.next = 0x4008 <= Address and Address < 0x400C
56 | Noise_CS.next = 0x400C <= Address and Address < 0x4010
57 | APU_CE.next = PHI2_CE and APU_CE_cnt
58 |
59 | PCM_out.next = PCM_pulse1 + PCM_pulse2 + PCM_noise + PCM_triangle
60 |
61 | return instances()
62 |
63 |
64 |
65 | def APU_FrameCounter(
66 | CLK, PHI2_CE, APU_CE, RW10, Address, Data_write,
67 | HalfFrame_CE, QuarterFrame_CE, Interrupt):
68 |
69 | timer = Signal(intbv()[15:0])
70 | Mode = Signal(False)
71 | InterruptInhibit = Signal(False)
72 |
73 | @always(CLK.posedge)
74 | def logic():
75 | if PHI2_CE and not RW10 and Address == 0x4017:
76 | Mode.next = Data_write[7]
77 | InterruptInhibit.next = Data_write[6]
78 |
79 | QuarterFrame_CE.next = False
80 | HalfFrame_CE.next = False
81 |
82 | if APU_CE:
83 | timer.next = timer + 1
84 |
85 | if timer == 3728:
86 | QuarterFrame_CE.next = True
87 | elif timer == 7456:
88 | HalfFrame_CE.next = True
89 | QuarterFrame_CE.next = True
90 | elif timer == 11186:
91 | QuarterFrame_CE.next = True
92 | elif not Mode and timer == 14914:
93 | HalfFrame_CE.next = True
94 | QuarterFrame_CE.next = True
95 | timer.next = 0
96 | elif Mode and timer == 18640:
97 | HalfFrame_CE.next = True
98 | QuarterFrame_CE.next = True
99 | timer.next = 0
100 |
101 | return instances()
102 |
103 |
104 | def APU_Envelope(
105 | CLK,
106 | QuarterFrame_CE,
107 |
108 | StartFlag,
109 | LoopFlag,
110 | ConstantFlag,
111 |
112 | VolumeDecay,
113 |
114 | VolumeOut
115 | ):
116 |
117 | divider = Signal(intbv()[4:0])
118 | volume = Signal(intbv()[4:0])
119 |
120 | @always(CLK.posedge)
121 | def logic():
122 | if QuarterFrame_CE:
123 | if StartFlag:
124 | #print "Start Envelope, length: ", VolumeDecay, " constant: ", ConstantFlag
125 | volume.next = 15
126 | divider.next = VolumeDecay
127 | else:
128 | if divider == 0:
129 | divider.next = VolumeDecay
130 | if volume != 0:
131 | volume.next = volume - 1
132 | else:
133 | if LoopFlag:
134 | volume.next = 15
135 | else:
136 | divider.next = divider - 1
137 |
138 | @always_comb
139 | def comb():
140 | if ConstantFlag:
141 | VolumeOut.next = VolumeDecay
142 | else:
143 | VolumeOut.next = volume
144 |
145 |
146 | return instances()
147 |
148 | def APU_Pulse(
149 | CLK, RSTN, PHI2_CE, APU_CE, RW10, Address, Data_write,
150 | ChipSelect, HalfFrame_CE, QuarterFrame_CE,
151 | PCM_out):
152 |
153 | DutyCycle = Signal(intbv()[2:0])
154 |
155 | EnvelopeDecay = Signal(intbv()[4:0])
156 | EnvelopeConstantFlag = Signal(False)
157 | EnvelopeStartFlag = Signal(False)
158 | EnvelopeVolume = Signal(intbv()[4:0])
159 |
160 | LengthCounterHalt = Signal(False)
161 | LengthCounterLoadFlag = Signal(False)
162 | LengthCounterLoad = Signal(intbv()[5:0])
163 | LengthCounterGate = Signal(False)
164 |
165 | lengthCounter = LengthCounter(CLK, HalfFrame_CE, LengthCounterHalt, LengthCounterLoad, LengthCounterLoadFlag, LengthCounterGate)
166 | envelope = APU_Envelope(CLK, QuarterFrame_CE,
167 | EnvelopeStartFlag, Signal(False), EnvelopeConstantFlag,
168 | EnvelopeDecay, EnvelopeVolume)
169 |
170 | TimerLoad = Signal(intbv()[11:0])
171 |
172 | sequencer = Signal(intbv("00001111"))
173 | timer = Signal(intbv()[11:0])
174 |
175 | @always(CLK.posedge)
176 | def logic():
177 | if not RSTN:
178 | sequencer.next = intbv("00001111")
179 | else:
180 | if QuarterFrame_CE:
181 | EnvelopeStartFlag.next = False
182 |
183 | LengthCounterLoadFlag.next = False
184 |
185 | if APU_CE:
186 | if timer == 0:
187 | sequencer.next = concat(sequencer[0], sequencer[8:1])
188 | PCM_out.next = EnvelopeVolume if sequencer[0] else 0x00
189 | if not LengthCounterGate:
190 | PCM_out.next = 0
191 | timer.next = TimerLoad
192 | else:
193 | timer.next = timer - 1
194 |
195 | if PHI2_CE and RW10 == 0 and ChipSelect:
196 | if Address[2:0] == 0x0:
197 | DutyCycle.next = Data_write[8:6]
198 |
199 | EnvelopeConstantFlag.next = Data_write[4]
200 | EnvelopeDecay.next = Data_write[4:0]
201 | elif Address[2:0] == 0x1:
202 | # Sweep unit unimplemented
203 | pass
204 | elif Address[2:0] == 0x2:
205 | TimerLoad.next[8:0] = Data_write
206 | elif Address[2:0] == 0x3:
207 | EnvelopeStartFlag.next = True
208 | TimerLoad.next[11:8] = Data_write[3:0]
209 | LengthCounterLoad.next = Data_write[8:3]
210 | LengthCounterLoadFlag.next = True
211 | return instances()
212 |
213 | def LengthCounter(
214 | CLK, HalfFrame_CE,
215 | LengthCounterHalt, LengthCounterLoad, LengthCounterLoadFlag,
216 | Enable_out
217 | ):
218 |
219 | LengthCounter = Signal(intbv()[8:0])
220 |
221 | # Lookup Table for Length Counter values
222 | LC_lut = (
223 | 10, 254, 20, 2, 40, 4, 80, 6,
224 | 160, 8, 60, 10, 14, 12, 26, 14,
225 | 12, 16, 24, 18, 48, 20, 96, 22,
226 | 192, 24, 72, 26, 16, 28, 32, 30
227 | )
228 |
229 | @always(CLK.posedge)
230 | def logic():
231 | if HalfFrame_CE:
232 | if LengthCounter > 0 and not LengthCounterHalt:
233 | LengthCounter.next = LengthCounter - 1
234 |
235 | if LengthCounterLoadFlag:
236 | LengthCounter.next = LC_lut[LengthCounterLoad]
237 |
238 | @always_comb
239 | def comb():
240 | Enable_out.next = LengthCounter > 0
241 |
242 | return instances()
243 |
244 | def APU_Noise(
245 | CLK, RSTN, PHI2_CE, APU_CE, RW10, Address, Data_write,
246 | ChipSelect, HalfFrame_CE, QuarterFrame_CE,
247 | PCM_out):
248 |
249 | timer_lut = (4, 8, 16, 32, 64, 96, 128, 160, 202, 254, 380, 508, 762, 1016, 2034, 4068)
250 | lfsr = Signal(intbv("111111111111111")[15:0])
251 | timer = Signal(intbv()[12:0])
252 |
253 | TimerLoad = Signal(intbv()[4:0])
254 | LFSRMode = Signal(False)
255 |
256 | LengthCounterHalt = Signal(False)
257 | LengthCounterLoadFlag = Signal(False)
258 | LengthCounterLoad = Signal(intbv()[5:0])
259 | LengthCounterGate = Signal(False)
260 |
261 | EnvelopeDecay = Signal(intbv()[4:0])
262 | EnvelopeConstantFlag = Signal(False)
263 | EnvelopeStartFlag = Signal(False)
264 | EnvelopeVolume = Signal(intbv()[4:0])
265 |
266 | lengthCounter = LengthCounter(CLK, HalfFrame_CE, LengthCounterHalt, LengthCounterLoad, LengthCounterLoadFlag, LengthCounterGate)
267 | envelope = APU_Envelope(CLK, QuarterFrame_CE, EnvelopeStartFlag, Signal(False), EnvelopeConstantFlag,
268 | EnvelopeDecay, EnvelopeVolume)
269 |
270 |
271 | @always(CLK.posedge)
272 | def logic():
273 | if not RSTN:
274 | lfsr.next = intbv("111111111111111")
275 | else:
276 | if QuarterFrame_CE:
277 | EnvelopeStartFlag.next = False
278 |
279 | LengthCounterLoadFlag.next = False
280 |
281 | if PHI2_CE and RW10 == 0 and ChipSelect:
282 | if Address[2:0] == 0x0:
283 | LengthCounterHalt.next = Data_write[5]
284 | EnvelopeConstantFlag.next = Data_write[4]
285 | EnvelopeDecay.next = Data_write[4:0]
286 | elif Address[2:0] == 0x2:
287 | LFSRMode.next = Data_write[7]
288 | TimerLoad.next[4:0] = Data_write[4:0]
289 | elif Address[2:0] == 0x3:
290 | EnvelopeStartFlag.next = True
291 | LengthCounterLoad.next = Data_write[8:3]
292 | LengthCounterLoadFlag.next = True
293 | if APU_CE:
294 | if timer == 0:
295 | fb_bit = bool(lfsr[1])
296 | if LFSRMode:
297 | fb_bit = lfsr[6]
298 | lfsr.next = concat(fb_bit ^ lfsr[0], lfsr[15:1])
299 | # This is the simple version, the MyHDL convertor does not like it
300 | #fb_bit = lfsr[0] ^ (lfsr[6] if LFSRMode else lfsr[1])
301 | #lfsr.next = concat(fb_bit, lfsr[15:1])
302 | PCM_out.next = EnvelopeVolume if lfsr[0] and LengthCounterGate else 0x00
303 | timer.next = timer_lut[TimerLoad]
304 | else:
305 | timer.next = timer - 1
306 |
307 | return instances()
308 |
309 |
310 | def APU_Triangle(
311 | CLK, RSTN, PHI2_CE, APU_CE, RW10, Address, Data_write,
312 | ChipSelect, HalfFrame_CE, QuarterFrame_CE,
313 | PCM_out):
314 |
315 | lut = (15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)
316 | sequencer = Signal(intbv()[5:0])
317 | timer = Signal(intbv(0)[11:0])
318 |
319 | linearCounter = Signal(intbv()[7:0])
320 | linearCounterLoad = Signal(intbv()[7:0])
321 |
322 | LengthCounterHalt = Signal(False)
323 | LengthCounterLoadFlag = Signal(False)
324 | LengthCounterLoad = Signal(intbv()[5:0])
325 | LengthCounterGate = Signal(False)
326 |
327 | TimerLoad = Signal(intbv()[11:0])
328 |
329 |
330 | lengthCounter = LengthCounter(CLK, HalfFrame_CE, LengthCounterHalt, LengthCounterLoad, LengthCounterLoadFlag, LengthCounterGate)
331 |
332 | @always(CLK.posedge)
333 | def logic():
334 | if QuarterFrame_CE:
335 | if LengthCounterHalt:
336 | linearCounter.next = linearCounterLoad
337 | elif linearCounter > 0:
338 | linearCounter.next = linearCounter - 1
339 |
340 |
341 | LengthCounterLoadFlag.next = False
342 |
343 | if PHI2_CE and RW10 == 0 and ChipSelect:
344 | if Address[2:0] == 0x0:
345 | LengthCounterHalt.next = Data_write[7]
346 | linearCounterLoad.next = Data_write[7:0]
347 | elif Address[2:0] == 0x2:
348 | TimerLoad.next[8:0] = Data_write
349 | elif Address[2:0] == 0x3:
350 | TimerLoad.next[11:8] = Data_write[3:0]
351 | LengthCounterLoad.next = Data_write[8:3]
352 | LengthCounterLoadFlag.next = True
353 |
354 | if APU_CE:
355 | if timer == 0:
356 | sequencer.next = (sequencer + 1) % 32
357 | if LengthCounterGate and linearCounter > 0:
358 | PCM_out.next = lut[sequencer]
359 | else:
360 | PCM_out.next = 0
361 | timer.next = TimerLoad
362 | else:
363 | timer.next = timer - 1
364 | return instances()
365 |
--------------------------------------------------------------------------------
/APU/apu_convert.py:
--------------------------------------------------------------------------------
1 | from myhdl import *
2 |
3 | from apu import APU_Main
4 | from cpu_bus import CPU_Bus
5 |
6 | def convert():
7 | cpu_bus = CPU_Bus()
8 | interrupt = Signal(False)
9 | pcm = Signal(intbv()[8:0])
10 | toVerilog(APU_Main, cpu_bus.CLK, cpu_bus.RSTN, cpu_bus.PHI2_CE, cpu_bus.RW10,
11 | cpu_bus.Address, cpu_bus.Data_read, cpu_bus.Data_write,
12 | interrupt, pcm)
13 |
14 | convert()
15 |
--------------------------------------------------------------------------------
/APU/apu_tb.py:
--------------------------------------------------------------------------------
1 | """
2 | Minimal Testbench with an APU_Pulse unit that is sweeped via CPU_Bus writes
3 | The generated sweep can be used to implement a FIR filter in AC97_Writer
4 | """
5 |
6 | from myhdl import *
7 | from apu import APU_Main
8 | from cpu_bus import CPU_Bus
9 | from ac97wav import AC97_WavWriter
10 | from clk_util import CLK_Gen
11 | from nsf import NSFSoftCPU
12 |
13 | NES_CLK_period = 46 # ns
14 | # constant AC97_CLK_period : time := 20.833333333333332 us; -- 48 kHz
15 | # constant CLK_period : time := 46.560848137510206 ns; -- 21.477272 MhZ
16 |
17 | import sys
18 | import os
19 |
20 | #assert len(sys.argv) >= 2, "Usage: {} FILE.NSF [SONGNUMBER]".format(sys.argv[0])
21 | rom_filename = sys.argv[1]
22 | base_filename, _ = os.path.splitext(rom_filename)
23 | start_song = 0 if len(sys.argv) < 3 else sys.argv[2]
24 | wav_filename = "output/{}-{}.wav".format(base_filename, start_song)
25 |
26 | def APU_TB():
27 | APU_Interrupt = Signal(False)
28 | PCM = Signal(intbv()[8:])
29 | cpu_bus = CPU_Bus()
30 |
31 | clk_gen = CLK_Gen(cpu_bus.CLK, NES_CLK_period)
32 | cpu_bus.PHI2_CE = Signal(True)
33 | phi2_cnt = Signal(0)
34 |
35 |
36 | ac97 = AC97_WavWriter(PCM, wav_filename)
37 | apu = APU_Main(cpu_bus.CLK, cpu_bus.RSTN, cpu_bus.PHI2_CE, cpu_bus.RW10,
38 | cpu_bus.Address, cpu_bus.Data_read, cpu_bus.Data_write,
39 | APU_Interrupt, PCM)
40 |
41 | cpu = NSFSoftCPU(rom_filename)
42 | cpu.subscribe_to_write(range(0x4000, 0x4017), cpu_bus.fake_write)
43 |
44 |
45 | cpu.setup(int(start_song))
46 |
47 | @always(cpu_bus.CLK.posedge)
48 | def cpu_step():
49 | if cpu_bus.PHI2_CE:
50 | cpu.play_cycle()
51 |
52 | phi2_cnt.next = (phi2_cnt + 1) % 12
53 | cpu_bus.PHI2_CE.next = phi2_cnt == 0
54 |
55 | return instances(), cpu_bus.instances
56 |
57 | Simulation(APU_TB()).run()
58 |
--------------------------------------------------------------------------------
/APU/clk_util.py:
--------------------------------------------------------------------------------
1 | from myhdl import *
2 |
3 | def CLK_Gen(CLK, period):
4 | @always(delay(period / 2))
5 | def gen():
6 | CLK.next = not CLK
7 |
8 | return gen
9 |
10 |
--------------------------------------------------------------------------------
/APU/cpu_bus.py:
--------------------------------------------------------------------------------
1 | from myhdl import *
2 |
3 |
4 | class CPU_Bus(object):
5 | def __init__(self, AddressWidth=16):
6 | self.CLK = Signal(False)
7 | self.RSTN = Signal(True)
8 | self.PHI2_CE = Signal(False)
9 | self.RW10 = Signal(True)
10 |
11 | self.Address = Signal(intbv()[AddressWidth:])
12 | self.Data_write = Signal(intbv()[8:])
13 | self.Data_read = Signal(intbv()[8:])
14 |
15 | self.Data_slaves = []
16 |
17 | self.write_queue = []
18 |
19 | @always(self.CLK.posedge)
20 | def write_proc():
21 | if self.PHI2_CE:
22 | if self.write_queue:
23 | a, d = self.write_queue.pop(0)
24 | self.Address.next = a
25 | self.Data_write.next = d
26 | self.RW10.next = 0
27 | else:
28 | self.RW10.next = 1
29 |
30 | self.instances = write_proc
31 |
32 |
33 | def RegisterSlaveAddress():
34 | assert False, "not implemented"
35 |
36 |
37 | def fake_write(self, address, data):
38 | print "Fake Write at $%04X: %02x" % (address, data)
39 | self.write_queue += [(address, data)]
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/APU/nsf.py:
--------------------------------------------------------------------------------
1 | from py65.memory import ObservableMemory
2 | from py65.devices.mpu6502 import MPU
3 |
4 | class NSFParser:
5 | def __init__(self, bytes):
6 | #import pdb; pdb.set_trace()
7 | assert bytes[0:5] == 'NESM\x1a', "Not a NSF File"
8 | assert bytes[5] == 1, "NSF Version != 0x01"
9 |
10 | self.num_songs = bytes[6]
11 | self.start_song = bytes[7]
12 | self.load_addr = bytes[0x08] + 0x100 * bytes[0x09]
13 | self.init_addr = bytes[0x0a] + 0x100 * bytes[0x0b]
14 | self.play_addr = bytes[0x0c] + 0x100 * bytes[0x0d]
15 |
16 | self.info_name = bytes[0x0e:0x2e].decode('ascii')
17 | self.info_artist = bytes[0x2e:0x4e].decode('ascii')
18 | self.info_copyright = bytes[0x4e:0x6e].decode('ascii')
19 |
20 | self.ntsc_ticks = bytes[0x6e] + 0x100 * bytes[0x6f]
21 | self.pal_ticks = bytes[0x78] + 0x100 * bytes[0x79]
22 |
23 | assert bytes[0x7a] == 0x00, "Not a NTSC Tune"
24 | assert bytes[0x7b] == 0x00, "Extra Sound Chips are not supported"
25 |
26 | assert bytes[0x70:0x78] == '\x00\x00\x00\x00\x00\x00\x00\x00', "Bank switching not supported at this time"
27 |
28 | self.data = bytes[0x80:]
29 |
30 |
31 | class NSFSoftCPU:
32 | NES_CLK_period = 46 * 12
33 |
34 | def __init__(self, filename):
35 | self.nsf = NSFParser(bytearray(file(filename).read()))
36 |
37 | # Set up Memory with Hooks
38 | self.mem = ObservableMemory()
39 | self.mem.write(self.nsf.load_addr, self.nsf.data)
40 | self.cpu = MPU(self.mem)
41 | self.deltaCallTime = 0
42 | self.totalCycles = 0
43 |
44 | def subscribe_to_write(self, range, callback):
45 | self.mem.subscribe_to_write(range, callback)
46 |
47 | # Call NSF Init code
48 | def setup(self, start_song = -1):
49 | if start_song == -1:
50 | start_song = self.nsf.start_song - 1
51 |
52 | # Push a special address -1 to the stack to implement function calls
53 | self.cpu.stPushWord(0x1337 - 1)
54 |
55 | # NSF Style init call
56 | self.cpu.a = start_song
57 | self.cpu.x = 0
58 | self.cpu.pc = self.nsf.init_addr
59 |
60 | while self.cpu.pc != 0x1337:
61 | self.cpu.step()
62 |
63 | # Execute 1 CPU Step, or wait to until enough cycles have passed
64 | def play_cycle(self):
65 | self.deltaCallTime = self.deltaCallTime + self.NES_CLK_period
66 |
67 | self.check_frame()
68 | if self.cpu.pc != 0x1337:
69 | self.totalCycles = self.totalCycles + 1
70 | if self.totalCycles == self.cpu.processorCycles:
71 | self.cpu.step()
72 |
73 |
74 | # Internal: Check if frame is completed and restart it periodically
75 | def check_frame(self):
76 | if self.cpu.pc == 0x1337:
77 | frame_time = self.nsf.ntsc_ticks * 1000
78 | if self.deltaCallTime >= frame_time:
79 | self.deltaCallTime = self.deltaCallTime - frame_time
80 | self.cpu.stPushWord(0x1337 - 1)
81 | self.cpu.pc = self.nsf.play_addr
82 | print "Frame Completed"
83 |
84 |
85 |
--------------------------------------------------------------------------------
/APU/smb.nsf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strfry/nesfpga/5059f4b2394c47a59518675b1a63adfc50ff1b63/APU/smb.nsf
--------------------------------------------------------------------------------
/CartridgeROM.vhd:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- Entity: CartridgeROM
3 | -- Date:2011-10-25
4 | -- Author: jonathansieber
5 | --
6 | -- Description ${cursor}
7 | --------------------------------------------------------------------------------
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | library std;
13 | use std.textio.all;
14 |
15 | entity CartridgeROM is
16 | port (
17 | clk : in std_logic; -- input clock, xx MHz.
18 | rstn : in std_logic;
19 |
20 | PRG_Address : in std_logic_vector(14 downto 0);
21 | PRG_Data : out std_logic_vector(7 downto 0);
22 |
23 | CHR_Address : in unsigned(13 downto 0);
24 | CHR_Data : out std_logic_vector(7 downto 0)
25 | );
26 | end CartridgeROM;
27 |
28 | architecture arch of CartridgeROM is
29 |
30 | type CHRRAMType is array(0 to 8191) of bit_vector(7 downto 0);
31 |
32 | impure function InitCHRRAMFromFile (RamFileName : in string) return CHRRAMType is
33 | FILE RamFile : text is in RamFileName;
34 | variable RamFileLine : line;
35 | variable RAM : CHRRAMType;
36 | begin
37 | for I in CHRRAMType'range loop
38 | readline (RamFile, RamFileLine);
39 | read (RamFileLine, RAM(I));
40 | end loop;
41 | return RAM;
42 | end function;
43 |
44 | signal CHRRAM : CHRRAMType := InitCHRRAMFromFile("roms/smb_chr.dat");
45 |
46 | type PRGRAMType is array(0 to 32767) of bit_vector(7 downto 0);
47 |
48 | impure function InitPRGRAMFromFile (RamFileName : in string) return PRGRAMType is
49 | FILE RamFile : text is in RamFileName;
50 | variable RamFileLine : line;
51 | variable RAM : PRGRAMType;
52 | begin
53 | for I in PRGRAMType'range loop
54 | readline (RamFile, RamFileLine);
55 | read (RamFileLine, RAM(I));
56 | end loop;
57 | return RAM;
58 | end function;
59 |
60 | signal PRGRAM : PRGRAMType := InitPRGRAMFromFile("roms/smb_prg.dat");
61 |
62 | begin
63 |
64 | process (clk)
65 | begin
66 | if rising_edge(clk) then
67 | CHR_Data <= to_stdlogicvector(CHRRAM(to_integer(CHR_Address(12 downto 0))));
68 |
69 | PRG_Data <= to_stdlogicvector(PRGRAM(to_integer(unsigned(PRG_Address))));
70 | end if;
71 | end process;
72 |
73 | end arch;
74 |
75 |
76 |
--------------------------------------------------------------------------------
/CartridgeROM_Coregen.vhd:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- Entity: CartridgeROM
3 | -- Date:2011-10-25
4 | -- Author: jonathansieber
5 | --
6 | -- Description ${cursor}
7 | --------------------------------------------------------------------------------
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | library std;
13 | use std.textio.all;
14 |
15 | Library UNISIM;
16 | use UNISIM.vcomponents.all;
17 |
18 | entity CartridgeROM is
19 | port (
20 | clk : in std_logic; -- input clock, xx MHz.
21 | rstn : in std_logic;
22 |
23 | PRG_Address : in std_logic_vector(14 downto 0);
24 | PRG_Data : out std_logic_vector(7 downto 0);
25 |
26 | CHR_Address : in unsigned(13 downto 0);
27 | CHR_Data : out std_logic_vector(7 downto 0)
28 | );
29 | end CartridgeROM;
30 |
31 |
32 | architecture arch of CartridgeROM is
33 | COMPONENT prg_rom_NESStress
34 | PORT (
35 | clka : IN STD_LOGIC;
36 | addra : IN STD_LOGIC_VECTOR(14 DOWNTO 0);
37 | douta : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
38 | );
39 | END COMPONENT;
40 |
41 | COMPONENT chr_rom_NEStress
42 | PORT (
43 | clka : IN STD_LOGIC;
44 | addra : IN STD_LOGIC_VECTOR(12 DOWNTO 0);
45 | douta : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
46 | );
47 | END COMPONENT;
48 |
49 | COMPONENT prg_rom_smb
50 | PORT (
51 | clka : IN STD_LOGIC;
52 | addra : IN STD_LOGIC_VECTOR(14 DOWNTO 0);
53 | douta : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
54 | );
55 | END COMPONENT;
56 |
57 | COMPONENT chr_rom_smb
58 | PORT (
59 | clka : IN STD_LOGIC;
60 | addra : IN STD_LOGIC_VECTOR(12 DOWNTO 0);
61 | douta : OUT STD_LOGIC_VECTOR(7 DOWNTO 0)
62 | );
63 | END COMPONENT;
64 | begin
65 | --
66 | --prg : prg_rom_NESStress
67 | -- PORT MAP (
68 | -- clka => clk,
69 | -- addra => PRG_Address,
70 | -- douta => PRG_Data
71 | -- );
72 | --
73 | --chr : chr_rom_NEStress
74 | -- PORT MAP (
75 | -- clka => clk,
76 | -- addra => std_logic_vector(CHR_Address(12 downto 0)),
77 | -- douta => CHR_Data
78 | -- );
79 |
80 | prg : prg_rom_smb
81 | PORT MAP (
82 | clka => clk,
83 | addra => PRG_Address,
84 | douta => PRG_Data
85 | );
86 |
87 | chr : chr_rom_smb
88 | PORT MAP (
89 | clka => clk,
90 | addra => std_logic_vector(CHR_Address(12 downto 0)),
91 | douta => CHR_Data
92 | );
93 |
94 | end arch;
95 |
96 |
97 |
--------------------------------------------------------------------------------
/ColorPalette.vhd:
--------------------------------------------------------------------------------
1 |
2 | library IEEE;
3 | use IEEE.STD_LOGIC_1164.ALL;
4 | use ieee.numeric_std.all;
5 |
6 | entity ColorPalette is
7 | Port ( ColorIndex : in unsigned (5 downto 0);
8 | Red : out STD_LOGIC_VECTOR (5 downto 0);
9 | Green : out STD_LOGIC_VECTOR (5 downto 0);
10 | Blue : out STD_LOGIC_VECTOR (5 downto 0));
11 | end ColorPalette;
12 |
13 | architecture Behavioral of ColorPalette is
14 |
15 | signal Entry : std_logic_vector(23 downto 0);
16 | begin
17 | -- Palettes taken from http://nesdev.parodius.com/nespalette.zip
18 |
19 | Red <= Entry(23 downto 18);
20 | Green <= Entry(15 downto 10);
21 | Blue <= Entry(7 downto 2);
22 |
23 |
24 | with to_integer(ColorIndex) select
25 | Entry <=
26 | X"727272" when 0,
27 | X"000080" when 1,
28 | X"08008A" when 2,
29 | X"2C007E" when 3,
30 | X"4A004E" when 4,
31 | X"500006" when 5,
32 | X"440000" when 6,
33 | X"260800" when 7,
34 | X"0A2000" when 8,
35 | X"002E00" when 9,
36 | X"003200" when 10,
37 | X"00260A" when 11,
38 | X"001C48" when 12,
39 | X"000000" when 13,
40 | X"000000" when 14,
41 | X"000000" when 15,
42 | X"A4A4A4" when 16,
43 | X"0038CE" when 17,
44 | X"3416EC" when 18,
45 | X"5E04DC" when 19,
46 | X"8C00B0" when 20,
47 | X"9A004C" when 21,
48 | X"901800" when 22,
49 | X"703600" when 23,
50 | X"4C5400" when 24,
51 | X"0E6C00" when 25,
52 | X"007400" when 26,
53 | X"006C2C" when 27,
54 | X"005E84" when 28,
55 | X"000000" when 29,
56 | X"000000" when 30,
57 | X"000000" when 31,
58 | X"FFFFFF" when 32,
59 | X"4C9CFF" when 33,
60 | X"7C78FF" when 34,
61 | X"A664FF" when 35,
62 | X"DA5AFF" when 36,
63 | X"F054C0" when 37,
64 | X"F06A56" when 38,
65 | X"D68610" when 39,
66 | X"BAA400" when 40,
67 | X"76C000" when 41,
68 | X"46CC1A" when 42,
69 | X"2EC866" when 43,
70 | X"34C2BE" when 44,
71 | X"3A3A3A" when 45,
72 | X"000000" when 46,
73 | X"000000" when 47,
74 | X"FFFFFF" when 48,
75 | X"B6DAFF" when 49,
76 | X"C8CAFF" when 50,
77 | X"DAC2FF" when 51,
78 | X"F0BEFF" when 52,
79 | X"FCBCEE" when 53,
80 | X"FAC2C0" when 54,
81 | X"F2CCA2" when 55,
82 | X"E6DA92" when 56,
83 | X"CCE68E" when 57,
84 | X"B8EEA2" when 58,
85 | X"AEEABE" when 59,
86 | X"AEE8E2" when 60,
87 | X"B0B0B0" when 61,
88 | X"000000" when 62,
89 | X"000000" when 63,
90 | X"000000" when others;
91 |
92 |
93 | end Behavioral;
94 |
95 |
--------------------------------------------------------------------------------
/Coregen/coregen.cgp:
--------------------------------------------------------------------------------
1 | # Date: Sun May 5 00:40:47 2013
2 |
3 | SET addpads = false
4 | SET asysymbol = false
5 | SET busformat = BusFormatAngleBracketNotRipped
6 | SET createndf = false
7 | SET designentry = Advanced
8 | SET device = xc5vlx50t
9 | SET devicefamily = virtex5
10 | SET flowvendor = Foundation_ISE
11 | SET formalverification = false
12 | SET foundationsym = false
13 | SET implementationfiletype = Ngc
14 | SET package = ff1136
15 | SET removerpms = false
16 | SET simulationfiles = Structural
17 | SET speedgrade = -2
18 | SET verilogsim = false
19 | SET vhdlsim = true
20 | SET workingdirectory = ./tmp/
21 |
22 | # CRC: cd767547
23 |
--------------------------------------------------------------------------------
/Genesys_NES.vhd:
--------------------------------------------------------------------------------
1 |
2 | library IEEE;
3 | use IEEE.STD_LOGIC_1164.all;
4 | use IEEE.NUMERIC_STD.all;
5 |
6 | use work.NES_Pack.all;
7 |
8 | entity Genesys_NES is
9 | port (
10 | CLK : in std_logic; -- input clock, 100 MHz.
11 | RSTN : in std_logic;
12 |
13 | HDMIHSync : out std_logic;
14 | HDMIVSync : out std_logic;
15 | HDMIDE : out std_logic;
16 | HDMICLKP : out std_logic;
17 | HDMICLKN : out std_logic;
18 | HDMID : out std_logic_vector(11 downto 0);
19 | HDMISCL : inout std_logic;
20 | HDMISDA : inout std_logic;
21 | HDMIRSTN : out std_logic;
22 |
23 | AUDCLK : in STD_LOGIC;
24 | AUDSDI : in STD_LOGIC;
25 | AUDSDO : out STD_LOGIC;
26 | AUDSYNC : out STD_LOGIC;
27 | AUDRST : out STD_LOGIC;
28 |
29 | JA : inout std_logic_vector(7 downto 0);
30 | JB : inout std_logic_vector(7 downto 0);
31 |
32 | BTN : in std_logic_vector(6 downto 0);
33 | SW : in std_logic_vector(7 downto 0);
34 | LED : out std_logic_vector(7 downto 0)
35 |
36 | );
37 | end Genesys_NES;
38 |
39 | architecture Behavioral of Genesys_NES is
40 | component NES_Mainboard is
41 | port (
42 | clk : in std_logic; -- approximation to NES mainboard clock 21.47727
43 | rstn : in std_logic;
44 |
45 | -- Framebuffer output
46 | FB_Address : out unsigned(15 downto 0); -- linear index in 256x240 pixel framebuffer
47 | FB_Color : out std_logic_vector(5 downto 0); -- Palette index of current color
48 | FB_DE : out std_logic; -- True when PPU is writing to the framebuffer
49 |
50 | APU_PCM : out std_logic_vector(7 downto 0);
51 |
52 | -- Controller input
53 | Controller_Strobe : out std_logic; -- Set shift register in controller with current buttons
54 | Controller1_Clock : out std_logic; -- Shift register by one bit on falling edge
55 | Controller2_Clock : out std_logic; -- Shift register by one bit on falling edge
56 |
57 | Controller1_Data0_N : in std_logic; -- Shift register highest bit
58 | Controller1_Data1_N : in std_logic; -- Not connected in standard controllers
59 | Controller1_Data2_N : in std_logic; -- Not connected in standard controllers
60 |
61 | Controller2_Data0_N : in std_logic; -- Shift register highest bit
62 | Controller2_Data1_N : in std_logic; -- Not connected in standard controllers
63 | Controller2_Data2_N : in std_logic; -- Not connected in standard controllers
64 |
65 | PinWithoutSemicolon : out std_logic
66 | );
67 | end component;
68 |
69 | component AC97_Output is
70 | port (
71 | CLK : in std_logic; -- 100 MHz Clock
72 | RSTN : in std_logic;
73 |
74 | -- AC97 Ports
75 |
76 | BITCLK : in STD_LOGIC;
77 | AUDSDI : in STD_LOGIC;
78 | AUDSDO : out STD_LOGIC;
79 | AUDSYNC : out STD_LOGIC;
80 | AUDRST : out STD_LOGIC;
81 |
82 | -- Asynchronous PCM Input
83 | PCM_in_left : in std_logic_vector(15 downto 0);
84 | PCM_in_right: in std_logic_vector(15 downto 0)
85 | );
86 | end component;
87 |
88 |
89 |
90 | signal NES_Clock : std_logic;
91 | signal TFT_Clock : std_logic; -- 25 MHz
92 |
93 | signal NES_Clock_cnt : unsigned(7 downto 0);
94 | signal TFT_Clock_cnt : unsigned(1 downto 0);
95 |
96 | signal FB_Address : unsigned(15 downto 0);
97 | signal FB_Color : std_logic_vector(5 downto 0);
98 | signal FB_DE : std_logic;
99 |
100 | signal HDMI_FB_Address : std_logic_vector(15 downto 0);
101 | signal HDMI_FB_Color : std_logic_vector(5 downto 0);
102 |
103 | type fb_ram_type is array(65535 downto 0) of std_logic_vector(5 downto 0);
104 | type FramebufferFileType is file of fb_ram_type;
105 |
106 | signal fb_ram : fb_ram_type := (others => "101010");
107 |
108 | signal APU_PCM : std_logic_vector(7 downto 0);
109 | signal AC97_PCM : std_logic_vector(15 downto 0);
110 | signal PCM_test : std_logic_vector(15 downto 0);
111 |
112 | signal Controller_Strobe : std_logic;
113 | signal Controller1_Clock : std_logic;
114 | signal Controller2_Clock : std_logic;
115 |
116 | signal Controller1_ShiftRegister : std_logic_vector(7 downto 0);
117 | begin
118 |
119 | TFT_Clock <= TFT_Clock_cnt(1);
120 |
121 | CLOCK_DIVIDER : process (CLK)
122 | variable ref : integer;
123 | begin
124 | if rising_edge(clk) then
125 | if RSTN = '0' then
126 | NES_Clock_cnt <= X"00";
127 | TFT_Clock_cnt <= "00";
128 | NES_Clock <= '0';
129 | else
130 | ref := to_integer(unsigned(SW));
131 | if NES_Clock_cnt = ref then
132 | NES_Clock_cnt <= X"00";
133 | NES_Clock <= not NES_Clock;
134 | else
135 | NES_Clock_cnt <= NES_Clock_cnt + 1;
136 | end if;
137 |
138 | TFT_Clock_cnt <= TFT_Clock_cnt + 1;
139 | end if;
140 | end if;
141 | end process;
142 |
143 | CONTROLLER_INPUT : process (NES_Clock)
144 | variable Controller1_Clock_delay : std_logic;
145 | begin
146 | if rising_edge(NES_Clock) then
147 | if Controller1_Clock = '1' and Controller1_Clock_delay = '0' then -- detect rising edge
148 | Controller1_ShiftRegister <= JB(3) & Controller1_ShiftRegister(7 downto 1);
149 | end if;
150 |
151 | Controller1_Clock_delay := Controller1_Clock;
152 | end if;
153 | end process;
154 |
155 | JA <= APU_PCM;
156 | JB(1) <= Controller1_Clock;
157 | JB(0) <= Controller_Strobe;
158 |
159 | LED <= Controller1_ShiftRegister;
160 |
161 |
162 |
163 | FRAMEBUFFER_READ : process (TFT_Clock)
164 | begin
165 | if rising_edge(TFT_Clock) then
166 | HDMI_FB_Color <= fb_ram(to_integer(unsigned(HDMI_FB_Address)));
167 | end if;
168 | end process;
169 |
170 | FRAMEBUFFER_WRITE : process (NES_Clock)
171 | begin
172 | if rising_edge(Nes_Clock) then
173 | if FB_DE = '1' then
174 | fb_ram(to_integer(unsigned(FB_Address))) <= FB_Color;
175 | end if;
176 | end if;
177 | end process;
178 |
179 | nes : NES_Mainboard port map (
180 | clk => NES_Clock,
181 | rstn => RSTN,
182 |
183 | FB_Address => FB_Address,
184 | FB_Color => FB_Color,
185 | FB_DE => FB_DE,
186 |
187 | APU_PCM => APU_PCM,
188 |
189 | Controller_Strobe => Controller_Strobe,
190 | Controller1_Clock => Controller1_Clock,
191 | Controller2_Clock => Controller2_Clock,
192 |
193 | Controller1_Data0_N => JB(3),
194 | Controller1_Data1_N => '1',
195 | Controller1_Data2_N => '1',
196 | Controller2_Data0_N => '1',
197 | Controller2_Data1_N => '1',
198 | Controller2_Data2_N => '1',
199 |
200 | PinWithoutSemicolon => open
201 | );
202 |
203 | hdmi : HDMIController
204 | port map (
205 | CLK => TFT_Clock,
206 | RSTN => RSTN,
207 | CLK_25 => TFT_Clock,
208 | HDMIHSync => HDMIHSync,
209 | HDMIVSync => HDMIVSync,
210 | HDMIDE => HDMIDE,
211 | HDMICLKP => HDMICLKP,
212 | HDMICLKN => HDMICLKN,
213 | HDMID => HDMID,
214 | HDMISCL => HDMISCL,
215 | HDMISDA => HDMISDA,
216 | HDMIRSTN => HDMIRSTN,
217 |
218 | FB_Address => HDMI_FB_Address,
219 | FB_Data => HDMI_FB_Color
220 |
221 | );
222 |
223 | AC97_PCM <= APU_PCM & X"00";
224 | --LED <= APU_PCM;
225 |
226 | process (CLK)
227 | begin
228 | if rising_edge(CLK) then
229 | --PCM_test <= std_logic_vector(unsigned(PCM_test) + 1);
230 | end if;
231 | end process;
232 |
233 | ac97 : AC97_Output
234 | port map (
235 | CLK => CLK,
236 | RSTN => RSTN,
237 | BITCLK => AUDCLK,
238 | AUDSDI => AUDSDI,
239 | AUDSDO => AUDSDO,
240 | AUDSYNC => AUDSYNC,
241 | AUDRST => AUDRST,
242 |
243 | PCM_in_left => AC97_PCM,
244 | PCM_in_right => AC97_PCM
245 | );
246 |
247 |
248 | end Behavioral;
249 |
250 |
--------------------------------------------------------------------------------
/HDMI/HDMIController.vhd:
--------------------------------------------------------------------------------
1 | library IEEE;
2 | use IEEE.STD_LOGIC_1164.ALL;
3 | use ieee.numeric_std.all;
4 |
5 | library UNISIM;
6 | use UNISIM.VComponents.all;
7 |
8 | entity HDMIController is
9 | port (
10 | CLK : in std_logic;
11 | RSTN : in std_logic;
12 |
13 | CLK_25 : in std_logic;
14 |
15 | HDMIHSync : OUT std_logic;
16 | HDMIVSync : OUT std_logic;
17 | HDMIDE : OUT std_logic;
18 | HDMICLKP : OUT std_logic;
19 | HDMICLKN : OUT std_logic;
20 | HDMID : OUT std_logic_vector(11 downto 0);
21 | HDMISCL : INOUT std_logic;
22 | HDMISDA : INOUT std_logic;
23 | HDMIRSTN : OUT std_logic;
24 |
25 | FB_Address : out std_logic_vector(15 downto 0);
26 | FB_Data : in std_logic_vector(5 downto 0)
27 |
28 | );
29 | end HDMIController;
30 |
31 | architecture Behavioral of HDMIController is
32 |
33 | component tft_interface
34 | port (
35 | TFT_Clk : in std_logic;
36 | TFT_Rst : in std_logic;
37 | Bus2IP_Clk : in std_logic;
38 | Bus2IP_Rst : in std_logic;
39 |
40 | HSYNC : in std_logic;
41 | VSYNC : in std_logic;
42 | DE : in std_logic;
43 | RED : in std_logic_vector(5 downto 0);
44 | GREEN : in std_logic_vector(5 downto 0);
45 | BLUE : in std_logic_vector(5 downto 0);
46 |
47 | TFT_HSYNC : out std_logic;
48 | TFT_VSYNC : out std_logic;
49 | TFT_DE : out std_logic;
50 | TFT_VGA_CLK : out std_logic;
51 | TFT_VGA_R : out std_logic_vector(5 downto 0);
52 | TFT_VGA_G : out std_logic_vector(5 downto 0);
53 | TFT_VGA_B : out std_logic_vector(5 downto 0);
54 |
55 | TFT_DVI_CLK_P : out std_logic;
56 | TFT_DVI_CLK_N : out std_logic;
57 | TFT_DVI_DATA : out std_logic_vector(11 downto 0);
58 |
59 | I2C_done : out std_logic;
60 | TFT_IIC_SCL_I : in std_logic;
61 | TFT_IIC_SCL_O : out std_logic;
62 | TFT_IIC_SCL_T : out std_logic;
63 | TFT_IIC_SDA_I : in std_logic;
64 | TFT_IIC_SDA_O : out std_logic;
65 | TFT_IIC_SDA_T : out std_logic;
66 |
67 | IIC_xfer_done : out std_logic;
68 | TFT_iic_xfer : in std_logic;
69 | TFT_iic_reg_addr : in std_logic_vector(0 to 7);
70 | TFT_iic_reg_data : in std_logic_vector(0 to 7)
71 | );
72 | end component;
73 |
74 | component ColorPalette is
75 | port (
76 | ColorIndex : in unsigned (5 downto 0);
77 | Red : out STD_LOGIC_VECTOR (5 downto 0);
78 | Green : out STD_LOGIC_VECTOR (5 downto 0);
79 | Blue : out STD_LOGIC_VECTOR (5 downto 0)
80 | );
81 | end component;
82 |
83 | signal HSYNC : std_logic;
84 | signal VSYNC : std_logic;
85 | signal DE : std_logic;
86 | signal RED : std_logic_vector(5 downto 0);
87 | signal GREEN : std_logic_vector(5 downto 0);
88 | signal BLUE : std_logic_vector(5 downto 0);
89 |
90 | signal TFT_IIC_SCL_I : std_logic;
91 | signal TFT_IIC_SCL_O : std_logic;
92 | signal TFT_IIC_SCL_T : std_logic;
93 | signal TFT_IIC_SDA_I : std_logic;
94 | signal TFT_IIC_SDA_O : std_logic;
95 | signal TFT_IIC_SDA_T : std_logic;
96 |
97 | signal HSYNC_cnt : integer := 0;
98 | signal VSYNC_cnt : integer := 0;
99 |
100 | signal FB_Address_int : integer;
101 |
102 | signal VSYNC_delay : std_logic_vector(0 to 3);
103 | signal RST : std_logic;
104 |
105 | begin
106 |
107 | HDMIRSTN <= RSTN;
108 | RST <= not RSTN;
109 |
110 |
111 |
112 | VSYNC_delay(2) <= '0' when VSYNC_cnt < 2 else '1';
113 | VSYNC <= VSYNC_delay(0);
114 |
115 | HSYNC <= '0' when HSYNC_cnt < 96 else '1';
116 |
117 | DE <= '1' when HSYNC_cnt >= 144 and HSYNC_cnt < 784 and VSYNC_cnt >= 33 and VSYNC_cnt < 513 else '0';
118 |
119 | process (VSYNC_cnt, HSYNC_cnt)
120 | variable addr_int : integer;
121 | variable HPOS, VPOS : integer;
122 | begin
123 | HPOS := (HSYNC_cnt - 144) / 2 - 32;
124 | VPOS := (VSYNC_cnt - 33) / 2;
125 | addr_int := VPOS * 256 + HPOS;
126 |
127 | if HPOS >= 0 and HPOS < 256 and VPOS >= 0 and VPOS < 240 then
128 | FB_Address <= std_logic_vector(to_unsigned(addr_int, FB_Address'length));
129 | else
130 | FB_Address <= (others => '0');
131 | end if;
132 | end process;
133 |
134 | process (CLK_25)
135 | begin
136 | if rising_edge(CLK_25) then
137 | VSYNC_delay(0 to 1) <= VSYNC_delay(1 to 2);
138 | end if;
139 | end process;
140 |
141 | process (CLK_25)
142 | begin
143 | if rising_edge(CLK_25) then
144 | if RSTN = '0' then
145 | HSYNC_cnt <= 0;
146 | VSYNC_cnt <= 0;
147 | else
148 | if HSYNC_cnt < 800 - 1 then
149 | HSYNC_cnt <= HSYNC_cnt + 1;
150 | else
151 | HSYNC_cnt <= 0;
152 | if VSYNC_cnt < 525 - 1 then
153 | VSYNC_cnt <= VSYNC_cnt + 1;
154 | else
155 | VSYNC_cnt <= 0;
156 | end if;
157 | end if;
158 | end if;
159 | end if;
160 | end process;
161 |
162 |
163 | IISC_SCL_BUF: IOBUF
164 | port map (
165 | I => TFT_IIC_SCL_O,
166 | O => TFT_IIC_SCL_I,
167 | T => TFT_IIC_SCL_T,
168 | IO => HDMISCL
169 | );
170 |
171 | IISC_SDA_BUF: IOBUF
172 | port map (
173 | I => TFT_IIC_SDA_O,
174 | O => TFT_IIC_SDA_I,
175 | T => TFT_IIC_SDA_T,
176 | IO => HDMISDA
177 | );
178 |
179 |
180 | interface : tft_interface
181 | port map (
182 | TFT_Clk => CLK_25,
183 | TFT_Rst => RST,
184 | Bus2IP_Clk => CLK,
185 | Bus2IP_Rst => RST,
186 | HSYNC => HSYNC,
187 | VSYNC => VSYNC,
188 | DE => DE,
189 | RED => RED,
190 | GREEN => GREEN,
191 | BLUE => BLUE,
192 |
193 |
194 | TFT_HSYNC => HDMIHSync,
195 | TFT_VSYNC => HDMIVSync,
196 | TFT_DE => HDMIDE,
197 |
198 | TFT_VGA_CLK => open,
199 | TFT_VGA_R => open,
200 | TFT_VGA_G => open,
201 | TFT_VGA_B => open,
202 |
203 | TFT_DVI_CLK_P => HDMICLKP,
204 | TFT_DVI_CLK_N => HDMICLKN,
205 | TFT_DVI_DATA => HDMID,
206 |
207 | I2C_done => open,
208 | TFT_IIC_SCL_I => TFT_IIC_SCL_I,
209 | TFT_IIC_SCL_O => TFT_IIC_SCL_O,
210 | TFT_IIC_SCL_T => TFT_IIC_SCL_T,
211 | TFT_IIC_SDA_I => TFT_IIC_SDA_I,
212 | TFT_IIC_SDA_O => TFT_IIC_SDA_O,
213 | TFT_IIC_SDA_T => TFT_IIC_SDA_T,
214 | IIC_xfer_done => open,
215 | TFT_iic_xfer => '0',
216 | TFT_iic_reg_addr => "--------",
217 | TFT_iic_reg_data => "--------"
218 | );
219 |
220 | PALETTE : ColorPalette
221 | port map (
222 | ColorIndex => unsigned(FB_Data),
223 | Red => RED,
224 | Green => GREEN,
225 | Blue => BLUE
226 | );
227 |
228 |
229 | end Behavioral;
230 |
231 |
232 |
233 |
--------------------------------------------------------------------------------
/NES_2A03/ClockDivider.vhd:
--------------------------------------------------------------------------------
1 | --Dan Leach
2 | --Clock divider (divide by twelve, seven cycle duty cycle)
3 | --Created 1/03/06
4 | --Initial revision
5 | --Modified 1/27/06
6 | --Changed if statements so that Clk_Out is set after the first input clock
7 | -- cycle. Also added another clause so that the counter is reset after
8 | -- 12 input cycles.
9 | --Set default/initial values of all pins
10 | --Modified sometime since then
11 | --Added PHI2 signal, inverse of PHI1/Clk_Out
12 | --Modified 6/27/06
13 | --Need another signal to use when reading data from the bus
14 | -- this means another signal goes high 2 input clock cycles
15 | -- after Clk_Out_Phi2 goes high
16 | --Modified 7/01/06
17 | --Need another signal for putting the address on the bus shortly after
18 | -- PHI1 (Clk_Out_Phi2) goes high
19 | library ieee;
20 | use ieee.std_logic_1164.all;
21 | use ieee.numeric_std.all;
22 |
23 |
24 | entity Clock_Divider is
25 | port(Clk_In, Reset_N, Enable : in std_logic;
26 | PHI1_CE : out std_logic;
27 | PHI2 : out std_logic;
28 | AddOK_CE : out std_logic;
29 | WriteOK_CE : out std_logic;
30 | ReadOK_CE : out std_logic
31 | );
32 | end Clock_Divider;
33 |
34 | architecture Behavior of Clock_Divider is
35 | constant Duty : unsigned (3 downto 0) := "0111"; --5 cycle duty cycle
36 | constant Total : unsigned (3 downto 0) := "1011";--"1011"; --12 cycles total
37 | constant WriteOK : unsigned (3 downto 0) := "0010"; --Read 2 cycles into Phi2
38 | constant ReadOK : unsigned (3 downto 0) := "0101"; --Read 3 cycles into Phi2
39 | constant AddOK : unsigned (3 downto 0) := "1000"; --Address valid 1 cycle into Phi1
40 | signal Count_Out_W : unsigned (3 downto 0);
41 |
42 |
43 | begin
44 |
45 | PHI1_CE <= '1' when Count_Out_W = Duty else '0';
46 | PHI2 <= '1' when Count_Out_W < Duty else '0';
47 | AddOK_CE <= '1' when Count_Out_W = AddOK else '0';
48 | ReadOK_CE <= '1' when Count_Out_W = ReadOK else '0';
49 | WriteOK_CE <= '1' when Count_Out_W = WriteOK else '0';
50 |
51 | process(Clk_In, Reset_N)
52 | begin
53 | if Reset_N = '0' then
54 | Count_Out_W <= (others => '0');
55 | elsif rising_edge(Clk_In) then
56 | if Enable = '1' then
57 | if Count_Out_W = Total then
58 | Count_Out_W <= (others => '0');
59 | else
60 | Count_Out_W <= Count_Out_W + 1;
61 | end if;
62 | end if;
63 | end if;
64 | end process;
65 |
66 | --
67 | -- Count : process(Clk_In, Reset_N)
68 | -- begin
69 | -- if (Reset_N = '0') then
70 | -- Clk_Out <= '0';
71 | -- Clk_Out_Phi2 <= '0';
72 | -- Clk_Out_ReadOK <= '0';
73 | -- Count_Out_W <= (others => '0');
74 | -- elsif (falling_edge(Clk_In)) then
75 | -- if (Enable = '1') then
76 | -- case Count_Out_W is
77 | -- when "0000" =>
78 | -- Clk_Out <= '0';
79 | -- Clk_Out_Phi2 <= '1';
80 | -- Count_Out_W <= Count_Out_W + 1;
81 | -- when WriteOK =>
82 | -- Clk_Out_WriteOK <= '1';
83 | -- Clk_Out_AddOK <= '0';
84 | -- Count_Out_W <= Count_Out_W + 1;
85 | -- when AddOK =>
86 | -- Clk_Out_AddOK <= '1';
87 | -- Count_Out_W <= Count_Out_W + 1;
88 | -- when Duty =>
89 | -- Clk_Out <= '1';
90 | -- Clk_Out_Phi2 <= '0';
91 | -- Clk_Out_AddOK <= '0';
92 | -- Clk_Out_WriteOK <= '0';
93 | -- Clk_Out_ReadOK <= '0';
94 | -- Count_Out_W <= Count_Out_W + 1;
95 | -- when ReadOK =>
96 | -- Clk_Out_ReadOK <= '1';
97 | -- Count_Out_W <= Count_Out_W + 1;
98 | -- when Total =>
99 | -- Clk_Out <= '1';
100 | -- Clk_Out_Phi2 <= '0';
101 | --
102 | -- Count_Out_W <= (others => '0');
103 | -- when others =>
104 | -- Count_Out_W <= Count_Out_W + 1;
105 | -- end case;
106 | -- end if;
107 | -- end if;
108 | -- end process;
109 | end Behavior;
--------------------------------------------------------------------------------
/NES_2A03/DanPack.vhd:
--------------------------------------------------------------------------------
1 | library IEEE;
2 | use IEEE.std_logic_1164.all;
3 | use IEEE.numeric_std.all;
4 | use work.T65_Pack.all;
5 |
6 | package Pack_2A03 is
7 | component T65
8 | port(
9 | Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65C816
10 | Res_n : in std_logic;
11 | Enable : in std_logic;
12 | Clk : in std_logic;
13 | Rdy : in std_logic;
14 | --Abort_n : in std_logic;
15 | IRQ_n : in std_logic;
16 | NMI_n : in std_logic;
17 | SO_n : in std_logic;
18 | R_W_n : out std_logic;
19 | Sync : out std_logic;
20 | ML_n : out std_logic;
21 | VP_n : out std_logic;
22 | VDA : out std_logic;
23 | VPA : out std_logic;
24 | T65Address : out std_logic_vector(15 downto 0);
25 | T65DataIn : in std_logic_vector(7 downto 0);
26 | T65DataOut : out std_logic_vector(7 downto 0)
27 | --Added for debugging
28 | --T65_LCycle : out std_logic_vector(2 downto 0);
29 | --T65_MCycle : out std_logic_vector(2 downto 0);
30 | --T65_InitialReset : out std_logic
31 | );
32 | end component;
33 |
34 | component Clock_Divider
35 | port(Clk_In, Reset_N, Enable : in std_logic;
36 | PHI1_CE : out std_logic;
37 | PHI2 : out std_logic;
38 | AddOK_CE : out std_logic;
39 | WriteOK_CE : out std_logic;
40 | ReadOK_CE : out std_logic
41 | );
42 | end component;
43 |
44 | component SRAM
45 | port(
46 | Clock : in std_logic;
47 | ChipSelect_N : in std_logic;
48 | WriteEnable_N : in std_logic;
49 | OutputEnable_N : in std_logic;
50 | Address : in std_logic_vector (10 downto 0);
51 | Data : inout std_logic_vector (7 downto 0)
52 | );
53 | end component;
54 |
55 | component APU_Main is
56 | port (
57 | CLK: in std_logic;
58 | RSTN : in std_logic;
59 | PHI2_CE: in std_logic;
60 | RW10: in std_logic;
61 | Address: in std_logic_vector(15 downto 0);
62 | Data_read: out std_logic_vector(7 downto 0);
63 | Data_write: in std_logic_vector(7 downto 0);
64 | Interrupt: out std_logic;
65 | PCM_out: out std_logic_vector(7 downto 0)
66 | );
67 | end component;
68 | end Pack_2A03;
69 |
--------------------------------------------------------------------------------
/NES_2A03/Dan_2A03.vhd:
--------------------------------------------------------------------------------
1 | --Dan Leach
2 | --2A03 for NES
3 | --Created 1/03/06
4 | --Initial revision
5 | library ieee;
6 | use ieee.std_logic_1164.all;
7 | use ieee.numeric_std.all;
8 | use work.Pack_2A03.all;
9 |
10 | entity NES_2A03 is
11 | port (
12 | Global_Clk : in std_logic; --input clock signal from NES mobo crystal
13 | Reset_N : in std_logic; --External Reset
14 | NMI_N : in std_logic;
15 | IRQ_N : in std_logic;
16 | Data : inout std_logic_vector(7 downto 0);
17 | Address : buffer std_logic_vector(15 downto 0);
18 | RW_10 : buffer std_logic; --low if writing, high if reading
19 |
20 | PHI2 : out std_logic; --Clock Divider Output
21 |
22 | --Controller Outputs
23 | CStrobe : out std_logic; --Controller Strobe Signal
24 | C1R_N : out std_logic; --low when reading controller 1
25 | C2R_N : out std_logic; --low when reading controller 2
26 |
27 | --Audio Outputs
28 | A_Rectangle : out std_logic; --Rectangle Wave Output (Mixed)
29 | A_Combined : out std_logic_vector(7 downto 0); --Triangle, Noise, And PCM (DPCM) Output
30 |
31 | --The following three signals represent the status of an internal register
32 | -- used in accessing the expansion port
33 | W_4016_1 : out std_logic;
34 | W_4016_2 : out std_logic;
35 |
36 | --Debugging
37 | --LCycle : out std_logic_vector(2 downto 0);
38 | --MCycle : out std_logic_vector(2 downto 0);
39 | --InitialReset : out std_logic;
40 | AddOKDebug : out std_logic;
41 | ReadOKDebug : out std_logic;
42 | WriteOKDebug : out std_logic;
43 | SRAMChipSelect_NDebug : out std_logic;
44 | SRAMWriteEnable_NDebug : out std_logic;
45 | SRAMOutputEnable_NDebug :out std_logic;
46 | SRAMReading : out std_logic;
47 | SRAMWriting : out std_logic
48 | );
49 | end NES_2A03;
50 |
51 | architecture Behavioral of NES_2A03 is
52 | --Clock Control
53 | signal PHI1_CE : std_logic;
54 | signal PHI2_Internal : std_logic;
55 |
56 | --Internal Status Registers
57 | signal Global_Enable : std_logic;
58 |
59 | --T65 Control
60 | signal T65Enable : std_logic;
61 | signal T65DMADisable : std_logic;
62 | signal T65RW_10 : std_logic := '0';
63 | signal T65Address : std_logic_vector (15 downto 0);
64 | signal T65DataIn : std_logic_vector(7 downto 0);
65 | signal T65DataOut : std_logic_vector(7 downto 0);
66 |
67 | --DMA
68 | signal TransferCount : unsigned (9 downto 0);
69 | signal TransferAddress : unsigned (15 downto 0);
70 | signal DMAEnable : std_logic;
71 | signal DMARW_10 : std_logic := '0';
72 | signal DMAAddress : std_logic_vector (15 downto 0) := "0000000000000000";
73 | signal DMADataIn : std_logic_vector(7 downto 0);
74 | signal DMADataOut : std_logic_vector(7 downto 0);
75 | signal DMAContinue : std_logic;
76 | signal DMADelay : unsigned (2 downto 0);
77 |
78 | --Bus Control
79 | signal BusControl : std_logic;
80 | signal RW_10Signal : std_logic;
81 | signal AddressSignal : std_logic_vector (15 downto 0);
82 | signal DataInSignal : std_logic_vector (7 downto 0);
83 | signal DataOutSignal : std_logic_vector (7 downto 0);
84 |
85 | --Bus Timing
86 | signal AddOKSignal : std_logic := '0';
87 | signal ReadOKSignal : std_logic := '0';
88 | signal WriteOKSignal : std_logic := '0';
89 |
90 | --Memory
91 | signal SRAMWriteSignal : std_logic;
92 | signal SRAM_Reading : std_logic;
93 | signal SRAM_Writing : std_logic;
94 | signal SRAM_CS_N : std_logic;
95 |
96 | --Controllers
97 | signal ControllerOnePoll : std_logic := '0';
98 | signal ControllerTwoPoll : std_logic := '0';
99 |
100 | begin
101 | --more or less constant assignments
102 | Global_Enable <= '1';
103 | PHI2 <= PHI2_Internal;
104 |
105 | T65Enable <= PHI1_CE and not T65DMADisable;
106 |
107 | SRAMWriteSignal <= not ReadOKSignal when RW_10 = '0' else '1';
108 | SRAM_CS_N <= not PHI2_Internal or (Address(15) or Address(14) or Address(13));
109 |
110 | --to be implemented later
111 | A_Rectangle <= '0';
112 | --A_Combined <= '0';
113 | W_4016_1 <= 'Z';
114 | W_4016_2 <= 'Z';
115 |
116 | --Debugging
117 | WriteOKDebug <= WriteOKSignal;
118 | AddOKDebug <= AddOKSignal;
119 | ReadOKDebug <= ReadOKSignal;
120 |
121 | SRAMWriteEnable_NDebug <= SRAMWriteSignal;
122 | SRAMOutputEnable_NDebug <= '0';
123 | SRAMChipSelect_NDebug <= (not PHI2_Internal) or (Address(15) or Address(14) or Address(13));
124 |
125 | SRAMReading <= SRAM_Reading;
126 | SRAMWriting <= SRAM_Writing;
127 |
128 | --Controllers
129 | ControllerOnePoll <= '1' when Address = x"4016" else '0';
130 | ControllerTwoPoll <= '1' when Address = x"4017" else '0';
131 | C1R_N <= not (ControllerOnePoll and RW_10 and PHI2_Internal);
132 | C2R_N <= not (ControllerTwoPoll and RW_10 and PHI2_Internal);
133 |
134 |
135 | process (Global_Clk)
136 | begin
137 | if rising_edge(Global_Clk) then
138 | if Address = X"4016" and RW_10 = '0' and ReadOKSignal = '1' then
139 | CStrobe <= Data(0);
140 | end if;
141 | end if;
142 | end process;
143 |
144 |
145 | RW_10Signal <= T65RW_10 when BusControl = '0' else DMARW_10;
146 | AddressSignal <= T65Address when BusControl = '0' else DMAAddress;
147 |
148 |
149 | T65DataIn <= DataInSignal when BusControl = '0' else "01010101"; --????
150 | DMADataIn <= DataInSignal when BusControl = '1' else "01010101";
151 |
152 | with BusControl select DataOutSignal <=
153 | T65DataOut when '0',
154 | DMADataOut when others;
155 |
156 | --Bus stuff
157 | process (Reset_N, Global_Clk)
158 | begin
159 | if (Reset_N = '0') then
160 | DMAEnable <= '0';
161 | Address <= x"0000";
162 | elsif rising_edge(Global_Clk) then
163 | if AddOKSignal = '1' then
164 | RW_10 <= RW_10Signal;
165 | Address <= AddressSignal;
166 | if (T65Address = x"4014") then
167 | DMAEnable <= '1';
168 | else
169 | DMAEnable <= '0';
170 | end if;
171 | end if;
172 | end if;
173 | end process;
174 |
175 | process (Global_Clk)
176 | begin
177 | if rising_edge(Global_Clk) then
178 | if RW_10Signal = '1' then --reading
179 | if (PHI2_Internal = '0') then
180 | Data <= DataInSignal; --might be causing big-ass problems
181 | else
182 | Data <= "ZZZZZZZZ";
183 | if (ReadOKSignal = '1') then
184 | DataInSignal <= Data;
185 | end if;
186 | end if;
187 | else --writing
188 | if PHI2_Internal = '1' and WriteOKSignal = '1' then
189 | Data <= DataOutSignal;
190 | DataInSignal <= Data;
191 | elsif PHI2_Internal = '0' then
192 | Data <= "ZZZZZZZZ";
193 | end if;
194 | end if;
195 | end if;
196 | end process;
197 |
198 | DMATransfer : process (Reset_N, Global_Clk)
199 | begin
200 | if (Reset_N = '0') then
201 | TransferCount <= "0000000000";
202 | TransferAddress <= "0000000000000000";
203 | DMAContinue <= '0';
204 | T65DMADisable <= '0';
205 | BusControl <= '0';
206 | DMARW_10 <= '1';
207 | --D
208 | elsif rising_edge(Global_Clk) then --may need to check timing here
209 | if PHI1_CE = '1' then
210 | if (DMAEnable = '1') then
211 | DMAContinue <= '1';
212 | end if;
213 | if (DMAContinue = '1') then
214 | case TransferCount is
215 | when "0000000000" =>
216 | DMAAddress <= T65DataOut & "00000000";
217 | TransferAddress <= unsigned(T65DataOut & "00000000") + 1;
218 | T65DMADisable <= '1';
219 | BusControl <= '1';
220 | TransferCount <= "0000000001";
221 | when "1000000000" =>
222 | T65DMADisable <= '0';
223 | BusControl <= '0';
224 | TransferCount <= "0000000000";
225 | DMAContinue <= '0';
226 | DMARW_10 <= '1'; --shit dude, it's amazing anything happened without this
227 | when others =>
228 | T65DMADisable <= '1';
229 | BusControl <= '1';
230 | TransferCount <= TransferCount + 1;
231 | case TransferCount(0) is
232 | when '0' =>
233 | DMARW_10 <= '1';
234 | DMAAddress <= std_logic_vector(TransferAddress);
235 | TransferAddress <= TransferAddress + 1;
236 | when others =>
237 | DMARW_10 <= '0';
238 | DMAAddress <= x"2004";
239 | DMADataOut <= DMADataIn;
240 | end case;
241 | end case;
242 | end if;
243 | end if;
244 | end if;
245 | end process DMATransfer;
246 |
247 | --Port Maps
248 | Clk_Div_12 : Clock_Divider
249 | port map (
250 | Clk_In => Global_Clk,
251 | Reset_N => Reset_N,
252 | Enable => Global_Enable,
253 | PHI1_CE => PHI1_CE,
254 | PHI2 => PHI2_Internal,
255 | AddOK_CE => AddOKSignal,
256 | WriteOK_CE => WriteOKSignal,
257 | ReadOK_CE => ReadOKSignal
258 | );
259 |
260 | NES_2A03 : T65
261 | port map (
262 | Mode => "00",
263 | Res_n => Reset_N,
264 | Enable => T65Enable,
265 | Clk => Global_Clk,
266 | Rdy => '1', --used for single-cycle execution, not used in 2A03
267 | --Abort_n => '0', --not used at all
268 | IRQ_n => IRQ_N,
269 | NMI_n => NMI_N,
270 | SO_n => '1', --Set Overflow, not used by NES
271 | R_W_n => T65RW_10,
272 | Sync => open, --used for single-cycle execution, not used in 2A03
273 | ML_n => open, --used in 65C816
274 | VP_n => open, --used in 65C816
275 | VDA => open, --used in 65C816
276 | VPA => open, --used in 65C816
277 | T65Address => T65Address,
278 | T65DataIn => T65DataIn,
279 | T65DataOut => T65DataOut
280 | );
281 |
282 | CPU_RAM : SRAM
283 | port map (
284 | Clock => Global_Clk,
285 | ChipSelect_N => SRAM_CS_N,
286 | WriteEnable_N => SRAMWriteSignal,
287 | OutputEnable_N => "not"(RW_10),
288 | Address => Address (10 downto 0),
289 | Data => Data
290 | );
291 |
292 | APU : APU_Main
293 | port map (
294 | CLK => Global_Clk,
295 | RSTN => Reset_N,
296 | PHI2_CE => ReadOKSignal,
297 | RW10 => RW_10,
298 | Address => Address,
299 | --Data_read => APU_Data,
300 | Data_read => open,
301 | Data_write => Data,
302 | Interrupt => open,
303 | PCM_out => A_Combined
304 | );
305 | end;
306 |
--------------------------------------------------------------------------------
/NES_2A03/SRAM.vhd:
--------------------------------------------------------------------------------
1 | --Dan Leach
2 | --2K SRAM for NES
3 | --Created 9/10/06
4 | -- Initial revision
5 | -- For simplicity, as on the NES itself, the ChipSelect line is used as a clock (Phi2).
6 | -- This effectively makes the RAM synchronous and thus simpler to implement.
7 | library ieee;
8 | use ieee.std_logic_1164.all;
9 | use ieee.numeric_std.all;
10 |
11 | entity SRAM is
12 | port(
13 | Clock : in std_logic;
14 | ChipSelect_N : in std_logic;
15 | WriteEnable_N : in std_logic;
16 | OutputEnable_N : in std_logic;
17 | Address : in std_logic_vector (10 downto 0);
18 | Data : inout std_logic_vector (7 downto 0)
19 | );
20 | end SRAM;
21 |
22 | architecture Behavioral of SRAM is
23 | -- Declare Memory type
24 | type Memory is array(0 to 2047) of std_logic_vector (7 downto 0);
25 | signal CPUMemory : Memory := (others => (others => 'U'));
26 |
27 | signal Data_out : std_logic_vector (7 downto 0);
28 |
29 | begin
30 |
31 | Data <= "ZZZZZZZZ" when ChipSelect_N = '1' or WriteEnable_N = '0' or OutputEnable_N = '1' else Data_out;
32 | --
33 | -- process (Clock)
34 | -- begin
35 | -- if rising_edge(Clock) then
36 | -- WriteEnable_d <= WriteEnable_N;
37 | -- if ChipSelect_N = '0' and OutputEnable_N = '0' then
38 | -- if WriteEnable_N = '0' and WriteEnable_d = '1' then
39 | -- CPUMemory(to_integer(unsigned(Address))) <= Data;
40 | -- else
41 | -- Data_out <= CPUMemory(to_integer(unsigned(Address)));
42 | -- end if;
43 | -- else
44 | -- end if;
45 | -- end if;
46 | -- end process;
47 | process (Clock)
48 | begin
49 | if rising_edge(Clock) then
50 | if WriteEnable_N = '0' and ChipSelect_N = '0' then
51 | CPUMemory(to_integer(unsigned(Address))) <= Data;
52 | end if;
53 |
54 | Data_out <= CPUMemory(to_integer(unsigned(Address)));
55 | end if;
56 | end process;
57 |
58 | end Behavioral;
59 |
--------------------------------------------------------------------------------
/NES_2A03/T65_ALU.vhd:
--------------------------------------------------------------------------------
1 | -- ****
2 | -- T65(b) core. In an effort to merge and maintain bug fixes ....
3 | --
4 | --
5 | -- Ver 300 Bugfixes by ehenciak added
6 | -- MikeJ March 2005
7 | -- Latest version from www.fpgaarcade.com (original www.opencores.org)
8 | --
9 | -- ****
10 | --
11 | -- 6502 compatible microprocessor core
12 | --
13 | -- Version : 0245
14 | --
15 | -- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org)
16 | --
17 | -- All rights reserved
18 | --
19 | -- Redistribution and use in source and synthezised forms, with or without
20 | -- modification, are permitted provided that the following conditions are met:
21 | --
22 | -- Redistributions of source code must retain the above copyright notice,
23 | -- this list of conditions and the following disclaimer.
24 | --
25 | -- Redistributions in synthesized form must reproduce the above copyright
26 | -- notice, this list of conditions and the following disclaimer in the
27 | -- documentation and/or other materials provided with the distribution.
28 | --
29 | -- Neither the name of the author nor the names of other contributors may
30 | -- be used to endorse or promote products derived from this software without
31 | -- specific prior written permission.
32 | --
33 | -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34 | -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
35 | -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36 | -- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
37 | -- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38 | -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39 | -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40 | -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41 | -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 | -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43 | -- POSSIBILITY OF SUCH DAMAGE.
44 | --
45 | -- Please report bugs to the author, but before you do so, please
46 | -- make sure that this is not a derivative work and that
47 | -- you have the latest version of this file.
48 | --
49 | -- The latest version of this file can be found at:
50 | -- http://www.opencores.org/cvsweb.shtml/t65/
51 | --
52 | -- Limitations :
53 | --
54 | -- File history :
55 | --
56 | -- 0245 : First version
57 | --
58 |
59 | library IEEE;
60 | use IEEE.std_logic_1164.all;
61 | use IEEE.numeric_std.all;
62 | use work.T65_Pack.all;
63 |
64 | entity T65_ALU is
65 | port(
66 | Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816
67 | Op : in std_logic_vector(3 downto 0);
68 | BusA : in std_logic_vector(7 downto 0);
69 | BusB : in std_logic_vector(7 downto 0);
70 | P_In : in std_logic_vector(7 downto 0);
71 | P_Out : out std_logic_vector(7 downto 0);
72 | Q : out std_logic_vector(7 downto 0)
73 | );
74 | end T65_ALU;
75 |
76 | architecture rtl of T65_ALU is
77 |
78 | -- AddSub variables (temporary signals)
79 | signal ADC_Z : std_logic;
80 | signal ADC_C : std_logic;
81 | signal ADC_V : std_logic;
82 | signal ADC_N : std_logic;
83 | signal ADC_Q : std_logic_vector(7 downto 0);
84 | signal SBC_Z : std_logic;
85 | signal SBC_C : std_logic;
86 | signal SBC_V : std_logic;
87 | signal SBC_N : std_logic;
88 | signal SBC_Q : std_logic_vector(7 downto 0);
89 |
90 | begin
91 |
92 | process (P_In, BusA, BusB) --determines flag status after addition, as well as result
93 | variable AL : unsigned(6 downto 0);
94 | variable AH : unsigned(6 downto 0);
95 | variable C : std_logic;
96 | begin
97 | AL := resize(unsigned(BusA(3 downto 0) & P_In(Flag_C)), 7) + resize(unsigned(BusB(3 downto 0) & "1"), 7);
98 | AH := resize(unsigned(BusA(7 downto 4) & AL(5)), 7) + resize(unsigned(BusB(7 downto 4) & "1"), 7);
99 |
100 | -- pragma translate_off
101 | if is_x(std_logic_vector(AL)) then AL := "0000000"; end if; --if AL cannot be determined it is set to 0
102 | if is_x(std_logic_vector(AH)) then AH := "0000000"; end if; --if AL cannot be determined it is set to 0
103 | -- pragma translate_on
104 | -- if both the high-order nybble and the low-order nybble are 0, then the zero flag is set
105 | if AL(4 downto 1) = 0 and AH(4 downto 1) = 0 then
106 | ADC_Z <= '1';
107 | else
108 | ADC_Z <= '0';
109 | end if;
110 | -- BCD Mode, not used in NES
111 | -- if AL(5 downto 1) > 9 and P_In(Flag_D) = '1' then
112 | -- AL(6 downto 1) := AL(6 downto 1) + 6;
113 | -- end if;
114 |
115 | C := AL(6) or AL(5);
116 | AH := resize(unsigned(BusA(7 downto 4) & C), 7) + resize(unsigned(BusB(7 downto 4) & "1"), 7);
117 |
118 | ADC_N <= AH(4);
119 | ADC_V <= (AH(4) xor BusA(7)) and not (BusA(7) xor BusB(7));
120 |
121 | -- pragma translate_off
122 | if is_x(std_logic_vector(AH)) then AH := "0000000"; end if;
123 | -- pragma translate_on
124 | -- BCD Mode, not used in NES
125 | -- if AH(5 downto 1) > 9 and P_In(Flag_D) = '1' then
126 | -- AH(6 downto 1) := AH(6 downto 1) + 6;
127 | -- end if;
128 |
129 | ADC_C <= AH(6) or AH(5);
130 |
131 | ADC_Q <= std_logic_vector(AH(4 downto 1) & AL(4 downto 1));
132 | end process;
133 |
134 | process (Op, P_In, BusA, BusB) --determines flag status after subtraction, as well as result
135 | variable AL : unsigned(6 downto 0);
136 | variable AH : unsigned(5 downto 0);
137 | variable C : std_logic;
138 | begin
139 | C := P_In(Flag_C) or not Op(0);
140 | AL := resize(unsigned(BusA(3 downto 0) & C), 7) - resize(unsigned(BusB(3 downto 0) & "1"), 6);
141 | AH := resize(unsigned(BusA(7 downto 4) & "0"), 6) - resize(unsigned(BusB(7 downto 4) & AL(5)), 6);
142 |
143 | -- pragma translate_off
144 | if is_x(std_logic_vector(AL)) then AL := "0000000"; end if;
145 | if is_x(std_logic_vector(AH)) then AH := "000000"; end if;
146 | -- pragma translate_on
147 |
148 | if AL(4 downto 1) = 0 and AH(4 downto 1) = 0 then
149 | SBC_Z <= '1';
150 | else
151 | SBC_Z <= '0';
152 | end if;
153 |
154 | SBC_C <= not AH(5);
155 | SBC_V <= (AH(4) xor BusA(7)) and (BusA(7) xor BusB(7));
156 | SBC_N <= AH(4);
157 | -- BCD Mode, not used in NES
158 | -- if P_In(Flag_D) = '1' then
159 | -- if AL(5) = '1' then
160 | -- AL(5 downto 1) := AL(5 downto 1) - 6;
161 | -- end if;
162 | -- AH := resize(unsigned(BusA(7 downto 4) & "0"), 6) - resize(unsigned(BusB(7 downto 4) & AL(6)), 6);
163 | -- if AH(5) = '1' then
164 | -- AH(5 downto 1) := AH(5 downto 1) - 6;
165 | -- end if;
166 | -- end if;
167 |
168 | SBC_Q <= std_logic_vector(AH(4 downto 1) & AL(4 downto 1));
169 | end process;
170 |
171 | process (Op, P_In, BusA, BusB,
172 | ADC_Z, ADC_C, ADC_V, ADC_N, ADC_Q,
173 | SBC_Z, SBC_C, SBC_V, SBC_N, SBC_Q)
174 | variable Q_t : std_logic_vector(7 downto 0);
175 | begin
176 | -- ORA, AND, EOR, ADC, NOP, LD, CMP, SBC
177 | -- ASL, ROL, LSR, ROR, BIT, LD, DEC, INC
178 | P_Out <= P_In;
179 | Q_t := BusA;
180 | case Op(3 downto 0) is
181 | when "0000" =>
182 | -- ORA
183 | Q_t := BusA or BusB;
184 | when "0001" =>
185 | -- AND
186 | Q_t := BusA and BusB;
187 | when "0010" =>
188 | -- EOR
189 | Q_t := BusA xor BusB;
190 | when "0011" =>
191 | -- ADC
192 | P_Out(Flag_V) <= ADC_V;
193 | P_Out(Flag_C) <= ADC_C;
194 | Q_t := ADC_Q;
195 | when "0101" | "1101" =>
196 | -- LDA
197 | when "0110" =>
198 | -- CMP
199 | P_Out(Flag_C) <= SBC_C;
200 | when "0111" =>
201 | -- SBC
202 | P_Out(Flag_V) <= SBC_V;
203 | P_Out(Flag_C) <= SBC_C;
204 | Q_t := SBC_Q;
205 | when "1000" =>
206 | -- ASL
207 | Q_t := BusA(6 downto 0) & "0";
208 | P_Out(Flag_C) <= BusA(7);
209 | when "1001" =>
210 | -- ROL
211 | Q_t := BusA(6 downto 0) & P_In(Flag_C);
212 | P_Out(Flag_C) <= BusA(7);
213 | when "1010" =>
214 | -- LSR
215 | Q_t := "0" & BusA(7 downto 1);
216 | P_Out(Flag_C) <= BusA(0);
217 | when "1011" =>
218 | -- ROR
219 | Q_t := P_In(Flag_C) & BusA(7 downto 1);
220 | P_Out(Flag_C) <= BusA(0);
221 | when "1100" =>
222 | -- BIT
223 | P_Out(Flag_V) <= BusB(6);
224 | when "1110" =>
225 | -- DEC
226 | Q_t := std_logic_vector(unsigned(BusA) - 1);
227 | when "1111" =>
228 | -- INC
229 | Q_t := std_logic_vector(unsigned(BusA) + 1);
230 | when others =>
231 | end case;
232 |
233 | case Op(3 downto 0) is
234 | when "0011" =>
235 | P_Out(Flag_N) <= ADC_N;
236 | P_Out(Flag_Z) <= ADC_Z;
237 | when "0110" | "0111" =>
238 | P_Out(Flag_N) <= SBC_N;
239 | P_Out(Flag_Z) <= SBC_Z;
240 | when "0100" =>
241 | when "1100" =>
242 | P_Out(Flag_N) <= BusB(7);
243 | if (BusA and BusB) = "00000000" then
244 | P_Out(Flag_Z) <= '1';
245 | else
246 | P_Out(Flag_Z) <= '0';
247 | end if;
248 | when others =>
249 | P_Out(Flag_N) <= Q_t(7);
250 | if Q_t = "00000000" then
251 | P_Out(Flag_Z) <= '1';
252 | else
253 | P_Out(Flag_Z) <= '0';
254 | end if;
255 | end case;
256 |
257 | Q <= Q_t;
258 | end process;
259 |
260 | end;
261 |
--------------------------------------------------------------------------------
/NES_2A03/T65_Pack.vhd:
--------------------------------------------------------------------------------
1 | -- ****
2 | -- T65(b) core. In an effort to merge and maintain bug fixes ....
3 | --
4 | --
5 | -- Ver 300 Bugfixes by ehenciak added
6 | -- MikeJ March 2005
7 | -- Latest version from www.fpgaarcade.com (original www.opencores.org)
8 | --
9 | -- ****
10 | --
11 | -- 65xx compatible microprocessor core
12 | --
13 | -- Version : 0246
14 | --
15 | -- Copyright (c) 2002 Daniel Wallner (jesus@opencores.org)
16 | --
17 | -- All rights reserved
18 | --
19 | -- Redistribution and use in source and synthezised forms, with or without
20 | -- modification, are permitted provided that the following conditions are met:
21 | --
22 | -- Redistributions of source code must retain the above copyright notice,
23 | -- this list of conditions and the following disclaimer.
24 | --
25 | -- Redistributions in synthesized form must reproduce the above copyright
26 | -- notice, this list of conditions and the following disclaimer in the
27 | -- documentation and/or other materials provided with the distribution.
28 | --
29 | -- Neither the name of the author nor the names of other contributors may
30 | -- be used to endorse or promote products derived from this software without
31 | -- specific prior written permission.
32 | --
33 | -- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
34 | -- AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
35 | -- THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36 | -- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE
37 | -- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38 | -- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39 | -- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40 | -- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41 | -- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42 | -- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43 | -- POSSIBILITY OF SUCH DAMAGE.
44 | --
45 | -- Please report bugs to the author, but before you do so, please
46 | -- make sure that this is not a derivative work and that
47 | -- you have the latest version of this file.
48 | --
49 | -- The latest version of this file can be found at:
50 | -- http://www.opencores.org/cvsweb.shtml/t65/
51 | --
52 | -- Limitations :
53 | --
54 | -- File history :
55 | --
56 |
57 | library IEEE;
58 | use IEEE.std_logic_1164.all;
59 |
60 | package T65_Pack is
61 | --flag enumeration
62 | constant Flag_C : integer := 0;
63 | constant Flag_Z : integer := 1;
64 | constant Flag_I : integer := 2;
65 | constant Flag_D : integer := 3;
66 | constant Flag_B : integer := 4;
67 | constant Flag_1 : integer := 5;
68 | constant Flag_V : integer := 6;
69 | constant Flag_N : integer := 7;
70 |
71 | component T65_MCode
72 | port(
73 | Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65816
74 | IR : in std_logic_vector(7 downto 0); -- instruction opcode
75 | MCycle : in std_logic_vector(2 downto 0); -- number of executed cycles for current instruction
76 | P : in std_logic_vector(7 downto 0); -- processor status
77 | LCycle : out std_logic_vector(2 downto 0); -- number of cycles instruction requires on original 6502
78 | ALU_Op : out std_logic_vector(3 downto 0); -- specifies ALU operation to perform
79 | Set_BusA_To : out std_logic_vector(2 downto 0); -- DI,A,X,Y,S,P
80 | Set_Addr_To : out std_logic_vector(1 downto 0); -- PC Adder,S,AD,BA
81 | Write_Data : out std_logic_vector(2 downto 0); -- DL,A,X,Y,S,P,PCL,PCH
82 | Jump : out std_logic_vector(1 downto 0); -- PC,++,DIDL,Rel
83 | BAAdd : out std_logic_vector(1 downto 0); -- None,DB Inc,BA Add,BA Adj
84 | BreakAtNA : out std_logic;
85 | ADAdd : out std_logic;
86 | AddY : out std_logic;
87 | PCAdd : out std_logic;
88 | Inc_S : out std_logic;
89 | Dec_S : out std_logic;
90 | LDA : out std_logic;
91 | LDP : out std_logic;
92 | LDX : out std_logic;
93 | LDY : out std_logic;
94 | LDS : out std_logic;
95 | LDDI : out std_logic;
96 | LDALU : out std_logic;
97 | LDAD : out std_logic;
98 | LDBAL : out std_logic;
99 | LDBAH : out std_logic;
100 | SaveP : out std_logic;
101 | Write : out std_logic
102 | );
103 | end component;
104 |
105 | component T65_ALU
106 | port(
107 | Mode : in std_logic_vector(1 downto 0); -- "00" => 6502, "01" => 65C02, "10" => 65C816
108 | Op : in std_logic_vector(3 downto 0);
109 | BusA : in std_logic_vector(7 downto 0);
110 | BusB : in std_logic_vector(7 downto 0);
111 | P_In : in std_logic_vector(7 downto 0);
112 | P_Out : out std_logic_vector(7 downto 0);
113 | Q : out std_logic_vector(7 downto 0)
114 | );
115 | end component;
116 |
117 | end;
118 |
--------------------------------------------------------------------------------
/NES_Mainboard.vhd:
--------------------------------------------------------------------------------
1 | library ieee;
2 | use ieee.std_logic_1164.all;
3 | use ieee.numeric_std.all;
4 | use work.NES_Pack.all;
5 |
6 | entity NES_Mainboard is
7 | port (
8 | clk : in std_logic; -- approximation to NES mainboard clock 21.47727 MHz
9 | rstn : in std_logic;
10 |
11 | -- Framebuffer output
12 | FB_Address : out unsigned(15 downto 0); -- linear index in 256x240 pixel framebuffer
13 | FB_Color : out std_logic_vector(5 downto 0); -- Palette index of current color
14 | FB_DE : out std_logic; -- True when PPU is writing to the framebuffer
15 |
16 | APU_PCM : out std_logic_vector(7 downto 0);
17 |
18 | -- Controller input
19 | Controller_Strobe : out std_logic; -- Set shift register in controller with current buttons
20 | Controller1_Clock : out std_logic; -- Shift register by one bit on falling edge
21 | Controller2_Clock : out std_logic; -- Shift register by one bit on falling edge
22 |
23 | Controller1_Data0_N : in std_logic; -- Shift register highest bit
24 | Controller1_Data1_N : in std_logic; -- Not connected in standard controllers
25 | Controller1_Data2_N : in std_logic; -- Not connected in standard controllers
26 |
27 | Controller2_Data0_N : in std_logic; -- Shift register highest bit
28 | Controller2_Data1_N : in std_logic; -- Not connected in standard controllers
29 | Controller2_Data2_N : in std_logic; -- Not connected in standard controllers
30 |
31 | PinWithoutSemicolon : out std_logic
32 | );
33 | end NES_Mainboard;
34 |
35 | architecture arch of NES_Mainboard is
36 |
37 | -- CPU Interrupt connected to PPU
38 | signal VBlank_NMI_n : std_logic;
39 |
40 | -- CPU Bus
41 |
42 | signal CPU_Address : std_logic_vector(15 downto 0);
43 | signal CPU_Data : std_logic_vector(7 downto 0);
44 | signal CPU_RW : std_logic;
45 | signal CPU_PHI2 : std_logic; -- High when CPU_Data is valid
46 |
47 | -- CPU Bus slave ports
48 |
49 | signal PPU_CPU_Data : std_logic_vector(7 downto 0);
50 | signal PRG_Data : std_logic_vector(7 downto 0);
51 | signal CPU_PPU_CS_n : std_logic;
52 |
53 | -- ChipSelect lines
54 | signal CPU_PRG_CS_n : std_logic;
55 |
56 | -- PPU Memory Bus
57 | signal CHR_Address : unsigned(13 downto 0);
58 | signal CHR_Data : std_logic_vector(7 downto 0);
59 |
60 | signal CPU_Controller1Read_N : std_logic;
61 | signal CPU_Controller2Read_N : std_logic;
62 |
63 | begin
64 |
65 | -- Access PPU in memory range 0x2000 to 0x2007
66 | CPU_PPU_CS_n <= '0' when CPU_Address(15 downto 3) = "0010000000000" and CPU_PHI2 = '1' else '1';
67 |
68 | -- Access Program ROM in range 0x8000 to 0xFFFF
69 | CPU_PRG_CS_n <= '0' when CPU_Address(15) = '1' and CPU_PHI2 = '1' else '1';
70 |
71 | -- Output inverting buffer is controlled by read access on reg $4016, e.g. Controller1Read_N
72 | Controller1_Clock <= not CPU_PHI2 when CPU_Controller1Read_N = '0' else '1';
73 |
74 | CPU_BUS_MUX : process(CPU_RW, PPU_CPU_Data, PRG_Data, CPU_PPU_CS_n, CPU_PRG_CS_n, CPU_Address, CPU_Controller1Read_N, CPU_Controller2Read_N)
75 | begin
76 | if CPU_RW = '1' then
77 | if CPU_PPU_CS_n = '0' then
78 | CPU_Data <= PPU_CPU_Data;
79 | elsif CPU_PRG_CS_n = '0' then
80 | CPU_Data <= PRG_Data;
81 | elsif CPU_Controller1Read_N = '0' then
82 | CPU_Data <= "01000" & not Controller1_Data2_N & not Controller1_Data1_N & not Controller1_Data0_N;
83 | elsif CPU_Controller2Read_N = '0' then
84 | CPU_Data <= "01000" & not Controller2_Data2_N & not Controller2_Data1_N & not Controller2_Data0_N;
85 | else
86 | CPU_Data <= (others => 'Z');
87 | end if;
88 | else
89 | CPU_Data <= (others => 'Z');
90 | end if;
91 | end process;
92 |
93 |
94 | CPU: NES_2A03
95 | port map (
96 | Global_Clk => clk,
97 | Reset_N => rstn,
98 | NMI_N => VBlank_NMI_n,
99 | IRQ_N => '1',
100 |
101 | Data => CPU_Data,
102 | Address => CPU_Address,
103 | RW_10 => CPU_RW,
104 |
105 | PHI2 => CPU_PHI2,
106 |
107 | CStrobe => Controller_Strobe,
108 | C1R_N => CPU_Controller1Read_N,
109 | C2R_N => CPU_Controller2Read_N,
110 |
111 | A_Rectangle => open,
112 | A_Combined => APU_PCM,
113 |
114 | W_4016_1 => open,
115 | W_4016_2 => open,
116 |
117 | AddOKDebug => open,
118 | ReadOKDebug => open,
119 | WriteOKDebug => open,
120 | SRAMChipSelect_NDebug => open,
121 | SRAMWriteEnable_NDebug => open,
122 | SRAMOutputEnable_NDebug => open,
123 | SRAMReading => open,
124 | SRAMWriting => open
125 | );
126 |
127 | PPU : NES_2C02
128 | port map (
129 | clk => clk,
130 | rstn => rstn,
131 |
132 | ChipSelect_n => CPU_PPU_CS_n,
133 | ReadWrite => CPU_RW,
134 | Address => CPU_Address(2 downto 0),
135 | Data_in => CPU_Data,
136 | Data_out => PPU_CPU_Data,
137 |
138 | CHR_Address => CHR_Address,
139 | CHR_Data => CHR_Data,
140 |
141 | VBlank_n => VBlank_NMI_n,
142 |
143 | FB_Address => FB_Address,
144 | FB_Color => FB_Color,
145 | FB_DE => FB_DE
146 | );
147 |
148 | Cartridge : CartridgeROM
149 | port map (
150 | clk => clk,
151 | rstn => rstn,
152 | PRG_Address => CPU_Address(14 downto 0),
153 | PRG_Data => PRG_Data,
154 | CHR_Address => CHR_Address,
155 | CHR_Data => CHR_Data
156 | );
157 |
158 | end architecture;
159 |
--------------------------------------------------------------------------------
/NES_Pack.vhd:
--------------------------------------------------------------------------------
1 | --------------------------------------------------------------------------------
2 | -- Entity: NESPack
3 | -- Date:2011-10-25
4 | -- Author: jonathansieber
5 | --
6 | -- Description ${cursor}
7 | --------------------------------------------------------------------------------
8 | library ieee;
9 | use ieee.std_logic_1164.all;
10 | use ieee.numeric_std.all;
11 |
12 | package NES_Pack is
13 |
14 | component NES_2A03 is
15 | port (
16 | Global_Clk : in std_logic; --input clock signal from NES mobo crystal
17 | Reset_N : in std_logic; --External Reset
18 | NMI_N : in std_logic;
19 | IRQ_N : in std_logic;
20 | Data : inout std_logic_vector(7 downto 0);
21 | Address : buffer std_logic_vector(15 downto 0);
22 | RW_10 : buffer std_logic; --low if writing, high if reading
23 | PHI2 : out std_logic; --Clock Divider Output
24 |
25 | --Controller Outputs
26 | CStrobe : out std_logic; --Controller Strobe Signal
27 | C1R_N : out std_logic; --low when reading controller 1
28 | C2R_N : out std_logic; --low when reading controller 2
29 |
30 | --Audio Outputs
31 | A_Rectangle : out std_logic; --Rectangle Wave Output (Mixed)
32 | A_Combined : out std_logic_vector(7 downto 0); --Triangle, Noise, And PCM (DPCM) Output
33 |
34 | --The following three signals represent the status of an internal register
35 | -- used in accessing the expansion port
36 | W_4016_1 : out std_logic;
37 | W_4016_2 : out std_logic;
38 |
39 | --Debugging
40 | --LCycle : out std_logic_vector(2 downto 0);
41 | --MCycle : out std_logic_vector(2 downto 0);
42 | --InitialReset : out std_logic;
43 | AddOKDebug : out std_logic;
44 | ReadOKDebug : out std_logic;
45 | WriteOKDebug : out std_logic;
46 | SRAMChipSelect_NDebug : out std_logic;
47 | SRAMWriteEnable_NDebug : out std_logic;
48 | SRAMOutputEnable_NDebug :out std_logic;
49 | SRAMReading : out std_logic;
50 | SRAMWriting : out std_logic
51 | );
52 | end component;
53 |
54 | component NES_2C02 is
55 | port (
56 | clk : in std_logic; -- input clock, 5,37 MHz.
57 | rstn : in std_logic;
58 |
59 | -- CPU Bus
60 | ChipSelect_n : in std_logic;
61 | ReadWrite : in std_logic; -- Write to PPU on 0
62 | Address : in std_logic_vector(2 downto 0);
63 | Data_in : in std_logic_vector(7 downto 0);
64 | Data_out : out std_logic_vector(7 downto 0);
65 |
66 | -- VRAM/VROM bus
67 | CHR_Address : out unsigned(13 downto 0);
68 | CHR_Data : in std_logic_vector(7 downto 0);
69 |
70 | VBlank_n : out std_logic; -- Tied to the CPU's Non-Maskable Interrupt (NMI)
71 |
72 | -- Framebuffer output
73 | FB_Address : out unsigned(15 downto 0); -- linear index in 256x240 pixel framebuffer
74 | FB_Color : out std_logic_vector(5 downto 0); -- Palette index of current color
75 | FB_DE : out std_logic -- True when PPU is writing to the framebuffer
76 | );
77 | end component;
78 |
79 | component CartridgeROM is
80 | port (
81 | clk : in std_logic; -- input clock, xx MHz.
82 | rstn : in std_logic;
83 |
84 | PRG_Address : in std_logic_vector(14 downto 0);
85 | PRG_Data : out std_logic_vector(7 downto 0);
86 |
87 | CHR_Address : in unsigned(13 downto 0);
88 | CHR_Data : out std_logic_vector(7 downto 0)
89 | );
90 | end component;
91 |
92 | component HDMIController is
93 | port (
94 | CLK : in std_logic;
95 | RSTN : in std_logic;
96 |
97 | CLK_25 : in std_logic;
98 |
99 | HDMIHSync : OUT std_logic;
100 | HDMIVSync : OUT std_logic;
101 | HDMIDE : OUT std_logic;
102 | HDMICLKP : OUT std_logic;
103 | HDMICLKN : OUT std_logic;
104 | HDMID : OUT std_logic_vector(11 downto 0);
105 | HDMISCL : INOUT std_logic;
106 | HDMISDA : INOUT std_logic;
107 | HDMIRSTN : OUT std_logic;
108 |
109 | FB_Address : out std_logic_vector(15 downto 0);
110 | FB_Data : in std_logic_vector(5 downto 0)
111 |
112 | );
113 | end component;
114 | end;
--------------------------------------------------------------------------------
/PPU/Loopy_Scrolling.vhd:
--------------------------------------------------------------------------------
1 | -- Scrolling and PPU Address registers
2 |
3 | -- The name Loopy refers to the nickname of the first person in the NES Community to accurately document this registers
4 | -- To save chip are, the PPU reused the counter registers used for Background Tiles as address register for CPU accesses.
5 | -- This can be utilized to change the scrolling mid-screen, which is used by many games, for example to implement a status bar.
6 |
7 | -- For more information visit http://wiki.nesdev.com/w/index.php/The_skinny_on_NES_scrolling
8 |
9 |
10 | library ieee;
11 | use ieee.std_logic_1164.all;
12 | use ieee.numeric_std.all;
13 |
14 | entity Loopy_Scrolling is
15 | port(
16 | clk : in std_logic;
17 | CE : in std_logic;
18 | rst : in std_logic;
19 |
20 | Loopy_t : in unsigned(14 downto 0); -- Temporary register driven by CPU Port
21 |
22 | Loopy_v : out unsigned(14 downto 0); -- Counting register
23 |
24 | -- Control lines
25 |
26 | ResetXCounter : in std_logic; -- Load initial scroll values
27 | ResetYCounter : in std_logic;
28 |
29 | IncXScroll : in std_logic; -- Increment coarse X, at every 8 pixels during scanline rendering
30 | IncYScroll : in std_logic; -- Increment Y, at scanline dot #256
31 |
32 | LoadAddress : in std_logic; -- Copy t to v, for CPU VRAM Access
33 | IncAddress : in std_logic; -- Increment Address during $2007 VRAM access
34 | AddressStep : in std_logic -- Increment in steps of 32 instead of 1
35 | );
36 | end entity Loopy_Scrolling;
37 |
38 | architecture RTL of Loopy_Scrolling is
39 |
40 | -- counter layout of the loopy registers
41 | --
42 | -- yyy NN YYYYY XXXXX
43 | -- ||| || ||||| +++++-- coarse X scroll
44 | -- ||| || +++++-------- coarse Y scroll
45 | -- ||| ++-------------- nametable select
46 | -- +++----------------- fine Y scroll
47 |
48 | signal Loopy : unsigned(14 downto 0) := (others => '0'); -- Internal output register
49 |
50 | alias FineYScroll : unsigned(2 downto 0) is Loopy(14 downto 12);
51 | alias YNametable : std_logic is Loopy(11);
52 | alias XNametable : std_logic is Loopy(10);
53 | alias CoarseYScroll : unsigned(4 downto 0) is Loopy(9 downto 5);
54 | alias CoarseXScroll : unsigned(4 downto 0) is Loopy(4 downto 0);
55 |
56 | begin
57 |
58 | Loopy_v <= Loopy;
59 |
60 | process(clk, rst) is
61 | variable sum : unsigned(7 downto 0);
62 | begin
63 | if rst = '0' then
64 | Loopy <= (others => '0');
65 | elsif rising_edge(clk) and CE = '1' then
66 | if LoadAddress = '1' then
67 | Loopy <= Loopy_t;
68 | elsif ResetXCounter = '1' then
69 | XNametable <= Loopy_t(10);
70 | CoarseXScroll <= Loopy_t(4 downto 0);
71 | elsif ResetYCounter = '1' then
72 | FineYScroll <= Loopy_t(14 downto 12);
73 | YNametable <= Loopy_t(11);
74 | CoarseYScroll <= Loopy_t(9 downto 5);
75 | elsif IncXScroll = '1' then
76 | sum := "00" & ((XNameTable & CoarseXScroll) + 1);
77 | XNameTable <= sum(5);
78 | CoarseXScroll <= sum (4 downto 0);
79 | elsif IncYScroll = '1' then
80 | if CoarseYScroll = 29 and FineYScroll = 31 then
81 | -- Coarse Y acts as a divide-by-30 counter
82 | YNametable <= not YNametable;
83 | CoarseYScroll <= "00000";
84 | FineYScroll <= "000";
85 | else
86 | sum := (CoarseYScroll & FineYScroll) + 1;
87 | CoarseYScroll <= sum(7 downto 3);
88 | FineYScroll <= sum(2 downto 0);
89 | end if;
90 | elsif IncAddress = '1' and AddressStep = '0' then
91 | Loopy <= Loopy + 1;
92 | elsif IncAddress = '1' and AddressStep = '1' then
93 | Loopy <= Loopy + 32;
94 | end if;
95 | end if;
96 | end process;
97 |
98 |
99 | end architecture RTL;
100 |
--------------------------------------------------------------------------------
/PPU/PPU.vhd:
--------------------------------------------------------------------------------
1 | --
2 |
3 |
4 | library ieee;
5 | use ieee.std_logic_1164.all;
6 | use ieee.numeric_std.all;
7 | use work.PPU_Pack.all;
8 |
9 | entity NES_2C02 is
10 | port(
11 | clk : in std_logic; -- approximation to NES mainboard clock 21.47727
12 | rstn : in std_logic;
13 |
14 | -- CPU Bus
15 | ChipSelect_n : in std_logic;
16 | ReadWrite : in std_logic; -- Write to PPU on 0
17 | Address : in std_logic_vector(2 downto 0);
18 | Data_in : in std_logic_vector(7 downto 0);
19 | Data_out : out std_logic_vector(7 downto 0);
20 |
21 | -- VRAM/VROM bus
22 | CHR_Address : out unsigned(13 downto 0);
23 | CHR_Data : in std_logic_vector(7 downto 0);
24 |
25 | VBlank_n : out std_logic; -- Tied to the CPU's Non-Maskable Interrupt (NMI)
26 |
27 | -- Framebuffer output
28 | FB_Address : out unsigned(15 downto 0); -- linear index in 256x240 pixel framebuffer
29 | FB_Color : out std_logic_vector(5 downto 0); -- Palette index of current color
30 | FB_DE : out std_logic -- True when PPU is writing to the framebuffer
31 |
32 | );
33 | end NES_2C02;
34 |
35 | architecture arch of NES_2C02 is
36 |
37 | -- Internal H/V Counters
38 | signal HPOS : integer range 0 to 340 := 0;
39 | signal VPOS : integer range 0 to 261 := 0;
40 |
41 | -- Nes Clock / 4 Clock divider
42 | signal CE_cnt : unsigned(1 downto 0) := "00";
43 | signal CE : std_logic;
44 |
45 | -- Specifies the scrolling offset within an 8 bit block
46 | signal Fine_HPOS : integer range 0 to 7;
47 |
48 |
49 | signal VBlankFlag : std_logic := '0';
50 | signal HitSpriteFlag : std_logic := '0';
51 |
52 | signal Status_2000 : std_logic_vector(7 downto 0) := "00000000";
53 | signal Status_2001 : std_logic_vector(7 downto 0) := "00000010";
54 |
55 | signal Data_in_d : std_logic_vector(7 downto 0) := "00000000";
56 | signal CPUPortDir : std_logic;
57 |
58 | signal ChipSelect_delay : std_logic;
59 |
60 |
61 | -- Internal Muxer outputs for read access on the PPU Memory Bus
62 | signal PPU_Address : unsigned(13 downto 0);
63 | signal PPU_Data : std_logic_vector(7 downto 0);
64 |
65 | -- This signal does not exist in the original hardware, which relies on bus capacitance
66 | signal CPUVRAM_WriteData : std_logic_vector(7 downto 0);
67 | signal CPUVRAM_Read : std_logic;
68 | signal CPUVRAM_Write : std_logic;
69 |
70 | type VRAMType is array (2047 downto 0) of std_logic_vector(7 downto 0);
71 | type PaletteRAMType is array (31 downto 0) of std_logic_vector(5 downto 0);
72 |
73 | signal VRAM : VRAMType := (others => "00000000");
74 | signal VRAM_Data : std_logic_vector(7 downto 0);
75 |
76 | signal PaletteRAM : PaletteRAMType := (
77 | 0 => "000000", 1 => "000001", 2 => "000010", 3 => "000011",
78 | 4 => "000100", 5 => "000101", 6 => "000110", 7 => "000111",
79 | 8 => "001000", 9 => "001001", 10 => "001010", 11 => "001011",
80 | 12 => "001100", 13 => "001101", 14 => "001110", 15 => "001111",
81 | 16 => "010000", 17 => "010001", 18 => "010010", 19 => "010011",
82 | 20 => "010100", 21 => "010101", 22 => "010110", 23 => "010111",
83 | 24 => "011000", 25 => "011001", 26 => "011010", 27 => "011011",
84 | 28 => "011100", 29 => "011101", 30 => "011110", 31 => "011111"
85 | );
86 |
87 | signal SpriteVRAM_Address : unsigned(13 downto 0);
88 | signal SpriteRAM_Address : unsigned(7 downto 0) := X"00";
89 | signal SpriteRAM_Data_in : std_logic_vector(7 downto 0);
90 | signal SpriteRAM_Data_out : std_logic_vector(7 downto 0);
91 | signal SpriteRAM_WriteEnable : std_logic;
92 |
93 | signal SpriteColor : unsigned(3 downto 0);
94 | signal SpriteForegroundPriority : std_logic;
95 | signal SpriteIsPrimary : std_logic;
96 | signal SpriteOverflowFlag : std_logic;
97 |
98 | signal TileVRAM_Address : unsigned(13 downto 0);
99 |
100 | signal TileColor : unsigned(3 downto 0);
101 |
102 |
103 | signal FineXScrolling : unsigned(2 downto 0) := "000";
104 | signal EnableTileRendering : std_logic;
105 |
106 | signal Loopy_t : unsigned(14 downto 0) := (others => '0');
107 | signal Loopy_v : unsigned(14 downto 0);
108 | signal ResetXCounter : std_logic;
109 | signal ResetYCounter : std_logic;
110 | signal IncXScroll : std_logic;
111 | signal IncYScroll : std_logic;
112 | signal LoadAddress : std_logic;
113 | signal IncAddress : std_logic;
114 | signal AddressStep : std_logic;
115 |
116 | begin
117 | CHR_Address <= PPU_Address;
118 |
119 | VBlank_n <= VBlankFlag nand Status_2000(7); -- Check on flag and VBlank Enable
120 |
121 |
122 | Fine_HPOS <= HPOS mod 8;
123 |
124 | CE <= '1' when CE_cnt = 0 else '0';
125 |
126 | process(clk)
127 | begin
128 | if rising_edge(clk) then
129 | CE_cnt <= CE_cnt + 1;
130 | end if;
131 | end process;
132 |
133 | process(clk)
134 | begin
135 | if rising_edge(clk) and CE = '1' then
136 | if HPOS < 341 - 1 then
137 | HPOS <= HPOS + 1;
138 | else
139 | HPOS <= 0;
140 | if VPOS < 262 - 1 then
141 | VPOS <= VPOS + 1;
142 | else
143 | VPOS <= 0;
144 | end if;
145 | end if;
146 | end if;
147 | end process;
148 |
149 | process(clk)
150 | variable color : integer range 0 to 31;
151 | begin
152 | if rising_edge(clk) and CE = '1' then
153 | FB_DE <= '0';
154 | if HPOS >= 0 and HPOS < 256 and VPOS < 240 then
155 | FB_DE <= '1';
156 | FB_Address <= to_unsigned(HPOS + VPOS * 256, FB_Address'length);
157 |
158 | if (SpriteForegroundPriority = '0' or TileColor(1 downto 0) = "00") and SpriteColor(1 downto 0) /= "00" then
159 | color := to_integer(SpriteColor) + 16;
160 | elsif TileColor(1 downto 0) /= "00" then
161 | color := to_integer(TileColor);
162 | else
163 | color := 16;
164 | end if;
165 |
166 | FB_Color <= PaletteRAM(color);
167 |
168 | --if SpritesFound > 0 then
169 | -- if SpriteCache(0).x - HPOS < 8 then FB_Color <= "101111"; end if;
170 | --end if;
171 |
172 |
173 | if VPOS >= 230 then
174 | FB_Color <= PaletteRAM(HPOS / 8);
175 | end if;
176 |
177 | else
178 | FB_Color <= "000000";
179 | end if;
180 | end if;
181 | end process;
182 |
183 | CPU_PORT : process(clk)
184 | variable PPUDATA_read : std_logic_vector(7 downto 0);
185 | begin
186 | if rising_edge(clk) then
187 | if CE = '1' and ChipSelect_N = '1' then
188 | if HPOS >= 0 and HPOS < 3 and VPOS = 240 then -- Start VBlank period
189 | VBlankFlag <= '1';
190 | elsif HPOS >= 0 and HPOS < 3 and VPOS = 261 then -- End VBlank Period
191 | VBlankFlag <= '0';
192 | HitSpriteFlag <= '0';
193 | end if;
194 |
195 | -- Hack: Increment sprite RAM address after write here, to avoid off-by-one condition
196 | if SpriteRAM_WriteEnable = '1' then
197 | SpriteRAM_Address <= SpriteRAM_Address + 1;
198 | end if;
199 |
200 | CPUVRAM_Write <= '0';
201 | CPUVRAM_Read <= '0';
202 | SpriteRAM_WriteEnable <= '0';
203 |
204 | IncAddress <= '0';
205 | AddressStep <= '0';
206 | LoadAddress <= '0';
207 |
208 | if CPUVRAM_Read = '1' or CPUVRAM_Write = '1' then
209 | IncAddress <= '1';
210 | AddressStep <= Status_2000(2);
211 |
212 | if CPUVRAM_Read = '1' then
213 | PPUDATA_read := PPU_Data;
214 | end if;
215 | end if;
216 |
217 | -- Check for Sprite 0 collision
218 | if TileColor(1 downto 0) /= "00" and SpriteColor(1 downto 0) /= "00" and SpriteIsPrimary = '1' then
219 | HitSpriteFlag <= '1';
220 | end if;
221 | end if;
222 |
223 | ChipSelect_delay <= ChipSelect_n;
224 | Data_in_d <= Data_in;
225 |
226 | -- Do reads on low CS, and writes on rising edge
227 | if ChipSelect_n = '1' and ChipSelect_delay = '0' then
228 | if ReadWrite = '0' then
229 | if Address = "000" then
230 | --Status_2000 <= Data_in_d;
231 | Status_2000 <= Data_in_d(7 downto 2) & "00";
232 | Loopy_t(11 downto 10) <= unsigned(Data_in_d(1 downto 0));
233 | elsif Address = "001" then
234 | Status_2001 <= Data_in_d;
235 | elsif Address = "011" then
236 | SpriteRAM_Address <= unsigned(Data_in_d);
237 | elsif Address = "100" then
238 | SpriteRAM_Data_in <= Data_in_d;
239 | SpriteRAM_WriteEnable <= '1';
240 | elsif Address = "101" then
241 | if CPUPortDir = '0' then
242 | FineXScrolling <= unsigned(Data_in_d(2 downto 0));
243 | Loopy_t(4 downto 0) <= unsigned(Data_in_d(7 downto 3));
244 | else
245 | Loopy_t(14 downto 12) <= unsigned(Data_in_d(2 downto 0));
246 | Loopy_t(9 downto 5) <= unsigned(Data_in_d(7 downto 3));
247 | end if;
248 | CPUPortDir <= not CPUPortDir;
249 | elsif Address = "110" then
250 | if CPUPortDir = '0' then
251 | Loopy_t(14 downto 8) <= "0" & unsigned(Data_in_d(5 downto 0));
252 | else
253 | Loopy_t(7 downto 0) <= unsigned(Data_in_d);
254 | LoadAddress <= '1';
255 | -- Loading to Loopy_v happens in Loopy_control process
256 | end if;
257 | CPUPortDir <= not CPUPortDir;
258 | elsif Address = "111" then
259 | CPUVRAM_Write <= '1';
260 | CPUVRAM_WriteData <= Data_in_d;
261 |
262 | -- Palette RAM is not actual RAM, just directly accessed registers, so implement it here
263 | if Loopy_v(14 downto 8) = X"3F" then
264 | PaletteRAM(to_integer(Loopy_v(4 downto 0))) <= Data_in_d(5 downto 0);
265 | end if;
266 | end if;
267 | elsif Address = "010" then
268 | VBlankFlag <= '0'; -- Reset flag at the end of read period
269 | CPUPortDir <= '0'; -- Reset 16 bit register selector
270 | end if;
271 | elsif ChipSelect_delay = '1' and ChipSelect_n = '0' and ReadWrite = '1' then
272 | if Address = "000" then
273 | Data_out <= Status_2000;
274 | elsif Address = "001" then
275 | Data_out <= Status_2001;
276 | elsif Address = "010" then
277 | Data_out <= (6 => HitSpriteFlag, 7 => VBlankFlag, others => '0');
278 | --Data_out <= (6 => HitSpriteFlag, 7 => '1', others => '0');
279 | elsif Address = "100" then
280 | Data_out <= SpriteRAM_Data_out;
281 | SpriteRAM_Address <= SpriteRAM_Address + 1;
282 | elsif Address = "111" then
283 | Data_out <= PPUDATA_read;
284 | CPUVRAM_Read <= '1';
285 | if Loopy_v(13 downto 8) = X"3F" then
286 | Data_out <= "00" & PaletteRAM(to_integer(Loopy_v(4 downto 0)));
287 | end if;
288 | else
289 | Data_out <= (others => 'X'); -- This should be a write only register
290 | end if;
291 | end if;
292 | end if;
293 | end process;
294 |
295 | PPU_ADDRESS_MUXER : process(CPUVRAM_Read, CPUVRAM_Write, Loopy_v, TileVRAM_Address, SpriteVRAM_Address, HPOS)
296 | begin
297 | if CPUVRAM_Read = '1' then
298 | PPU_Address <= Loopy_v(13 downto 0);
299 | elsif CPUVRAM_Write = '1' then
300 | PPU_Address <= Loopy_v(13 downto 0);
301 | elsif HPOS < 256 or HPOS >= 320 then
302 | PPU_Address <= TileVRAM_Address;
303 | else
304 | PPU_Address <= SpriteVRAM_Address;
305 | end if;
306 | end process;
307 |
308 | PPU_DATA_MUXER : process(PPU_Address, VRAM_Data, PaletteRAM, CHR_Data)
309 | begin
310 | -- The cartridge has tri-state access to the address lines A10/A11,
311 | -- so it can either provide additional 2k of SRAM, or tie them to 0
312 | -- to mirror the address range of the upper nametables to the lower ones
313 |
314 | -- Super Mario Brothers selects vertical mirroring (A11 tied down),
315 | -- so thats what we are doing here for now
316 | if PPU_Address(13 downto 12) = "10" then -- VRAM
317 | PPU_Data <= VRAM_Data;
318 | else
319 | -- Default to external PPU Data
320 | PPU_Data <= CHR_Data;
321 | end if;
322 | end process;
323 |
324 | -- The nametable VRAM was an extern SRAM IC in the NES,
325 | -- here it is implemented internally with BRAM
326 |
327 | INTERNAL_VRAM : process(clk)
328 | begin
329 | if rising_edge(clk) then
330 | if CE = '1' and CPUVRAM_Write = '1' and PPU_Address(13 downto 12) = "10" then
331 | VRAM(to_integer(PPU_Address(10 downto 0))) <= CPUVRAM_WriteData;
332 | end if;
333 | VRAM_Data <= VRAM(to_integer(PPU_Address(10 downto 0)));
334 | end if;
335 | end process;
336 |
337 | SPRITE_SEL : SpriteSelector
338 | port map(
339 | CLK => CLK,
340 | CE => CE,
341 | RSTN => RSTN,
342 | HPOS => HPOS,
343 | VPOS => VPOS,
344 | PatternTableAddressOffset => Status_2000(3),
345 | SpriteColor => SpriteColor,
346 | SpriteForegroundPriority => SpriteForegroundPriority,
347 | SpriteIsPrimary => SpriteIsPrimary,
348 | SpriteOverflowFlag => SpriteOverflowFlag,
349 | VRAM_Address => SpriteVRAM_Address,
350 | VRAM_Data => PPU_Data,
351 | SpriteRAM_Address => SpriteRAM_Address,
352 | SpriteRAM_Data_in => SpriteRAM_Data_in,
353 | SpriteRAM_Data_out => SpriteRAM_Data_out,
354 | SpriteRAM_WriteEnable => SpriteRAM_WriteEnable
355 | );
356 |
357 | TILE_FETCHER : TileFetcher
358 | port map (
359 | CLK => CLK,
360 | CE => CE,
361 | RSTN => RSTN,
362 | Loopy_v => Loopy_v,
363 | FineXScrolling => FineXScrolling,
364 | EnableRendering => EnableTileRendering,
365 | PatternTableAddressOffset => Status_2000(4),
366 | Fine_HPOS => Fine_HPOS,
367 | VRAM_Address => TileVRAM_Address,
368 | VRAM_Data => PPU_Data,
369 | TileColor => TileColor);
370 |
371 |
372 |
373 |
374 |
375 | Loopy_Scrolling_1 : Loopy_Scrolling
376 | port map (
377 | clk => CLK,
378 | CE => CE,
379 | rst => RSTN,
380 | Loopy_t => Loopy_t,
381 | Loopy_v => Loopy_v,
382 | ResetXCounter => ResetXCounter,
383 | ResetYCounter => ResetYCounter,
384 | IncXScroll => IncXScroll,
385 | IncYScroll => IncYScroll,
386 | LoadAddress => LoadAddress,
387 | IncAddress => IncAddress,
388 | AddressStep => AddressStep);
389 |
390 | Loopy_control : process (HPOS, VPOS, Address, ReadWrite, ChipSelect_N, Status_2001)
391 | begin
392 | ResetXCounter <= '0';
393 | ResetYCounter <= '0';
394 | IncXScroll <= '0';
395 | IncYScroll <= '0';
396 |
397 | EnableTileRendering <= '0';
398 |
399 | if Status_2001(3) = '1' then
400 | if VPOS = 261 then
401 | if HPOS >= 280 and HPOS < 304 then
402 | ResetYCounter <= '1';
403 | end if;
404 | elsif VPOS < 240 then
405 | if (HPOS >= 320 and HPOS < 336) or (HPOS >= 0 and HPOS < 256) then
406 | EnableTileRendering <= '1';
407 | if HPOS mod 8 = 7 then
408 | IncXScroll <= '1';
409 | end if;
410 | elsif HPOS = 256 then
411 | IncYScroll <= '1';
412 | elsif HPOS = 257 then
413 | ResetXCounter <= '1';
414 | end if;
415 | end if;
416 | end if;
417 | end process Loopy_Control;
418 |
419 |
420 | end arch;
421 |
--------------------------------------------------------------------------------
/PPU/PPU_Pack.vhd:
--------------------------------------------------------------------------------
1 | library ieee;
2 | use ieee.std_logic_1164.all;
3 | use ieee.numeric_std.all;
4 |
5 |
6 | package PPU_Pack is
7 | component TileFetcher
8 | port (
9 | CLK : in std_logic;
10 | CE : in std_logic;
11 | RSTN : in std_logic;
12 | Loopy_v : in unsigned(14 downto 0);
13 | FineXScrolling : in unsigned(2 downto 0);
14 | EnableRendering : in std_logic;
15 | PatternTableAddressOffset : in std_logic;
16 | Fine_HPOS : in integer range 0 to 7;
17 | VRAM_Address : out unsigned(13 downto 0);
18 | VRAM_Data : in std_logic_vector(7 downto 0);
19 | TileColor : out unsigned(3 downto 0));
20 | end component;
21 |
22 | component SpriteSelector is
23 | port (
24 | CLK : in std_logic;
25 | CE : in std_logic;
26 | RSTN : in std_logic;
27 |
28 | HPOS : in integer range 0 to 340;
29 | VPOS : in integer range 0 to 261;
30 |
31 | PatternTableAddressOffset : in std_logic;
32 |
33 | SpriteColor : out unsigned(3 downto 0);
34 | SpriteForegroundPriority : out std_logic;
35 | SpriteIsPrimary : out std_logic;
36 |
37 | SpriteOverflowFlag : out std_logic;
38 |
39 | VRAM_Address : out unsigned(13 downto 0);
40 | VRAM_Data : in std_logic_vector(7 downto 0);
41 |
42 | SpriteRAM_Address : in unsigned(7 downto 0);
43 | SpriteRAM_Data_in : in std_logic_vector(7 downto 0);
44 | SpriteRAM_Data_out : out std_logic_vector(7 downto 0);
45 | SpriteRAM_WriteEnable : in std_logic
46 | );
47 | end component;
48 |
49 | component Loopy_Scrolling
50 | port (
51 | clk : in std_logic;
52 | CE : in std_logic;
53 | rst : in std_logic;
54 | Loopy_t : in unsigned(14 downto 0);
55 | Loopy_v : out unsigned(14 downto 0);
56 | ResetXCounter : in std_logic;
57 | ResetYCounter : in std_logic;
58 | IncXScroll : in std_logic;
59 | IncYScroll : in std_logic;
60 | LoadAddress : in std_logic;
61 | IncAddress : in std_logic;
62 | AddressStep : in std_logic);
63 | end component;
64 | end package;
65 |
--------------------------------------------------------------------------------
/PPU/SpriteSelector.vhd:
--------------------------------------------------------------------------------
1 |
2 | library ieee;
3 | use ieee.std_logic_1164.all;
4 | use ieee.numeric_std.all;
5 |
6 | entity SpriteSelector is
7 | port (
8 | CLK : in std_logic;
9 | CE : in std_logic;
10 | RSTN : in std_logic;
11 |
12 | HPOS : in integer range 0 to 340;
13 | VPOS : in integer range 0 to 261;
14 |
15 | PatternTableAddressOffset : in std_logic;
16 |
17 | -- Selector output
18 | SpriteColor : out unsigned(3 downto 0); -- Palette index of sprite at current VPOS/HPOS pixel position
19 | SpriteForegroundPriority : out std_logic; -- When '0', Sprite is only drawn when background is transparent ("00" Color)
20 | SpriteIsPrimary : out std_logic; -- Is '1' when the current output results from object #0, used for collision detection flag
21 |
22 | SpriteOverflowFlag : out std_logic; -- When more than 8 Sprites are detected on a scanline, this flag is set until the next VBlank period
23 |
24 | VRAM_Address : out unsigned(13 downto 0) := (others => '0');
25 | VRAM_Data : in std_logic_vector(7 downto 0);
26 |
27 | SpriteRAM_Address : in unsigned(7 downto 0);
28 | SpriteRAM_Data_in : in std_logic_vector(7 downto 0);
29 | SpriteRAM_Data_out : out std_logic_vector(7 downto 0);
30 | SpriteRAM_WriteEnable : in std_logic
31 | );
32 | end SpriteSelector;
33 |
34 |
35 | architecture arch of SpriteSelector is
36 |
37 | -- Sprite RAM, also called (primary) OAM (Object Attribute Memory) is organized in 4 bytes per sprite,
38 | -- so 64 Sprites totalling 256 bytes can be described at a time.
39 | -- Memory layout of a Sprite Entry:
40 | -- RAM(SpriteNum * 4 + 0) = Y position, evaluated one scanline before the sprite is rendered
41 | -- RAM(SpriteNum * 4 + 1) = Tile Index
42 | -- RAM(SpriteNum * 4 + 2) = (7 => Y-Flip, 6 => X-Flip, 5 => BGPriority, (1 downto 0) => ColorPalette)
43 | -- RAM(SpriteNum * 4 + 3) = X position
44 |
45 |
46 | -- Each scanline, while memory access is reserved to the Background Tile Pipeline, the Sprite Selector
47 | -- selects up to 8 sprites from the OAM to be drawn in the next scanline and stores the necessary
48 | -- information in the "sprite temporary buffer".
49 | --
50 | -- After this period, the Sprite Selector gains read access to the PPU Memory bus and reads the
51 | -- corresponding tile patterns and writes them to the "secondary" OAM.
52 |
53 |
54 | -- The TempLineBuffer stores the result of the sprite evaluation phase. The MEMFETCH process
55 | -- reads 2 bytes from the Pattern Table, optionally flips them, and writes them to the Secondary OAM
56 | -- along with the remaining attributes.
57 |
58 | type TempLineBufferEntry is record
59 | patternIndex : unsigned(7 downto 0);
60 | ydiff : unsigned(7 downto 0);
61 | x : unsigned(7 downto 0);
62 | palette : unsigned(1 downto 0);
63 | xflip : std_logic;
64 | foreground : std_logic;
65 | primary : std_logic;
66 | end record;
67 |
68 | constant TempLineBufferDefault : TempLineBufferEntry := (
69 | patternIndex => X"FF", ydiff => X"FF", x => X"FF",
70 | palette => "11", others => '1'
71 | );
72 |
73 | type TempLineBufferType is array(7 downto 0) of TempLineBufferEntry;
74 | signal TempLineBuffer : TempLineBufferType := (others => TempLineBufferDefault);
75 |
76 |
77 | -- This Datatype corresponds to the "secondary OAM"
78 | type LineBufferEntry is record
79 | x : unsigned(7 downto 0);
80 | pattern0 : unsigned(7 downto 0);
81 | pattern1 : unsigned(7 downto 0);
82 | palette : unsigned(1 downto 0);
83 | foreground : std_logic;
84 | primary : std_logic;
85 | end record;
86 |
87 | constant LineBufferDefault : LineBufferEntry := (
88 | x => X"00", pattern0 => X"00", pattern1 => X"00",
89 | palette => "00", foreground => '0', primary => '0'
90 | );
91 |
92 | type LineBufferType is array(7 downto 0) of LineBufferEntry;
93 | signal SpriteLineBuffer : LineBufferType := (others => LineBufferDefault);
94 |
95 |
96 | type SpriteRAMType is array(255 downto 0) of std_logic_vector(7 downto 0);
97 | signal SpriteRAM : SpriteRAMType := (
98 | 0 => X"30", 1 => X"23", 2 => "11111111", 3 => X"30",
99 | 4 => X"30", 5 => X"33", 6 => "11111111", 7 => X"50",
100 | 8 => X"50", 9 => X"25", 10 => "11111111", 11 => X"50",
101 | 12 => X"70", 13 => X"55", 14 => "11111111", 16 => X"30",
102 | others => X"FF");
103 |
104 | signal NumSpritesFound : integer range 0 to 8 := 0;
105 |
106 | -- reverse Function used to implement the X-Flip feature
107 | function reverse(p: unsigned) return unsigned is
108 | variable temp: unsigned(p'reverse_range);
109 | variable result: unsigned(p'range);
110 | begin
111 | for i in p'range loop
112 | temp(i) := p(i);
113 | end loop;
114 | result := temp;
115 | return result;
116 | end;
117 |
118 | begin
119 |
120 | SPRITE_RAM_PORT_A : process (clk)
121 | begin
122 | if rising_edge(clk) and CE = '1' then
123 | SpriteRAM_Data_out <= SpriteRAM(to_integer(SpriteRAM_Address));
124 |
125 | if (SpriteRAM_WriteEnable = '1') then
126 | SpriteRAM(to_integer(SpriteRAM_Address)) <= SpriteRAM_Data_in;
127 | end if;
128 | end if;
129 | end process;
130 |
131 | -- The Line Buffer contains up to 8 sprites, select the first one with non-zero color
132 | PIXEL_MUX : process (SpriteLineBuffer, HPOS)
133 | variable sprite : LineBufferEntry;
134 | variable xpos : integer;
135 | variable patternColor : unsigned(1 downto 0);
136 | begin
137 | SpriteColor <= "0000";
138 | SpriteForegroundPriority <= '1';
139 | SpriteIsPrimary <= '0';
140 |
141 | for i in 7 downto 0 loop -- Loop backwards to prioritize the first entry, as it is written last
142 | sprite := SpriteLineBuffer(i);
143 | xpos := to_integer(sprite.x);
144 | if sprite.x = 0 then
145 | patternColor := sprite.pattern1(0) & sprite.pattern0(0);
146 |
147 | if patternColor /= "00" then
148 | SpriteColor <= unsigned(sprite.palette & patternColor);
149 | SpriteForegroundPriority <= sprite.foreground;
150 | SpriteIsPrimary <= sprite.primary;
151 | end if;
152 | end if;
153 | end loop;
154 | end process;
155 |
156 |
157 | SPRITE_LOOKUP : process (clk, rstn)
158 | variable attributeByte : std_logic_vector(7 downto 0);
159 | variable CurrentSpriteIndex : integer;
160 | begin
161 | if rstn = '0' then
162 | --SpriteCache <= (others => (others => (others => '0')));
163 | --CurrentSpriteIndex <= 0;
164 | elsif rising_edge(clk) and CE = '1' then
165 |
166 | if VPOS = 240 and HPOS = 0 then
167 | SpriteOverflowFlag <= '0';
168 | end if;
169 |
170 | if HPOS = 340 then
171 | NumSpritesFound <= 0;
172 | TempLineBuffer <= (others => TempLineBufferDefault);
173 | CurrentSpriteIndex := 0;
174 | elsif HPOS < 256 and VPOS < 240 and NumSpritesFound < 8 then
175 | -- Sprite Lookup phase (8 out of 64 selection)
176 |
177 | CurrentSpriteIndex := HPOS / 4;
178 |
179 | case HPOS mod 4 is
180 | when 0 =>
181 | TempLineBuffer(NumSpritesFound).ydiff <= VPOS - unsigned(SpriteRAM(CurrentSpriteIndex * 4));
182 | when 1 =>
183 | if TempLineBuffer(NumSpritesFound).ydiff < 8 then
184 | TempLineBuffer(NumSpritesFound).patternIndex <= unsigned(SpriteRAM(CurrentSpriteIndex * 4 + 1));
185 | end if;
186 | when 2 =>
187 | if TempLineBuffer(NumSpritesFound).ydiff < 8 then
188 | attributeByte := SpriteRAM(CurrentSpriteIndex * 4 + 2);
189 | TempLineBuffer(NumSpritesFound).palette <= unsigned(attributeByte(1 downto 0));
190 | TempLineBuffer(NumSpritesFound).foreground <= attributeByte(5);
191 | TempLineBuffer(NumSpritesFound).xflip <= attributeByte(6);
192 | TempLineBuffer(NumSpritesFound).primary <= '0';
193 | if CurrentSpriteIndex = 0 then
194 | TempLineBuffer(NumSpritesFound).primary <= '1';
195 | end if;
196 | end if;
197 | when 3 =>
198 | if TempLineBuffer(NumSpritesFound).ydiff < 8 then
199 | TempLineBuffer(NumSpritesFound).x <= unsigned(SpriteRAM(CurrentSpriteIndex * 4 + 3));
200 | NumSpritesFound <= NumSpritesFound + 1;
201 | --CurrentSpriteIndex <= CurrentSpriteIndex + 1;
202 | end if;
203 | when others =>
204 | end case;
205 |
206 | end if;
207 | end if;
208 | end process;
209 |
210 |
211 | SPRITE_MEMFETCH : process (clk)
212 | variable currentSprite : integer range 0 to 7;
213 | variable patternAddress : unsigned(13 downto 0);
214 | variable fetchedByte : unsigned(7 downto 0);
215 | begin
216 | if rising_edge(clk) and CE = '1' then
217 | if HPOS >= 0 and HPOS < 256 then
218 | for i in 7 downto 0 loop
219 | if SpriteLineBuffer(i).x > 0 then
220 | SpriteLineBuffer(i).x <= SpriteLineBuffer(i).x - 1;
221 | else
222 | SpriteLineBuffer(i).pattern0 <= SpriteLineBuffer(i).pattern0 srl 1;
223 | SpriteLineBuffer(i).pattern1 <= SpriteLineBuffer(i).pattern1 srl 1;
224 | end if;
225 | end loop;
226 | elsif HPOS >= 256 and HPOS < 288 then
227 | currentSprite := (HPOS - 256) / 4;
228 | patternAddress := "0" & PatternTableAddressOffset &
229 | TempLineBuffer(currentSprite).patternIndex & (TempLineBuffer(currentSprite).ydiff(3 downto 0));
230 |
231 | if currentSprite < NumSpritesFound then
232 | case HPOS mod 4 is
233 | when 0 =>
234 | VRAM_Address <= patternAddress;
235 | when 1 =>
236 | fetchedByte := unsigned(VRAM_Data);
237 | if TempLineBuffer(currentSprite).xflip = '0' then
238 | fetchedByte := reverse(fetchedByte);
239 | end if;
240 |
241 | SpriteLineBuffer(currentSprite).pattern0 <= fetchedByte;
242 |
243 | VRAM_Address <= patternAddress + 8;
244 | when 2 =>
245 | fetchedByte := unsigned(VRAM_Data);
246 | if TempLineBuffer(currentSprite).xflip = '0' then
247 | fetchedByte := reverse(fetchedByte);
248 | end if;
249 |
250 | SpriteLineBuffer(currentSprite).pattern1 <= fetchedByte;
251 | SpriteLineBuffer(currentSprite).x <= TempLineBuffer(currentSprite).x;
252 | SpriteLineBuffer(currentSprite).palette <= TempLineBuffer(currentSprite).palette;
253 | SpriteLineBuffer(currentSprite).primary <= TempLineBuffer(currentSprite).primary;
254 | SpriteLineBuffer(currentSprite).foreground <= TempLineBuffer(currentSprite).foreground;
255 |
256 | when others =>
257 | end case;
258 | end if;
259 |
260 | end if;
261 |
262 | end if;
263 | end process;
264 | end arch;
265 |
--------------------------------------------------------------------------------
/PPU/TileFetcher.vhd:
--------------------------------------------------------------------------------
1 | library IEEE;
2 | use IEEE.STD_LOGIC_1164.all;
3 |
4 | use IEEE.NUMERIC_STD.all;
5 |
6 | entity TileFetcher is
7 | port(
8 | CLK : in std_logic;
9 | CE : in std_logic;
10 | RSTN : in std_logic;
11 |
12 | Loopy_v : in unsigned(14 downto 0);
13 | FineXScrolling : in unsigned(2 downto 0);
14 |
15 | EnableRendering : in std_logic;
16 | PatternTableAddressOffset : in std_logic;
17 |
18 | Fine_HPOS : integer range 0 to 7;
19 |
20 | VRAM_Address : out unsigned(13 downto 0);
21 | VRAM_Data : in std_logic_vector(7 downto 0);
22 |
23 | TileColor : out unsigned(3 downto 0)
24 | );
25 | end TileFetcher;
26 |
27 | architecture Behavioral of TileFetcher is
28 | signal TilePattern0 : std_logic_vector(15 downto 0) := (others => '0');
29 | signal TilePattern1 : std_logic_vector(15 downto 0) := (others => '0');
30 | signal TileAttribute0 : unsigned(7 downto 0) := (others => '0');
31 | signal TileAttribute1 : unsigned(7 downto 0) := (others => '0');
32 |
33 | signal AttributeLatch0 : std_logic;
34 | signal AttributeLatch1 : std_logic;
35 |
36 | signal NextPattern0 : std_logic_vector(7 downto 0);
37 | signal NextPattern1 : std_logic_vector(7 downto 0);
38 | signal NextAttribute0 : std_logic;
39 | signal NextAttribute1 : std_logic;
40 |
41 |
42 | begin
43 | process(TileAttribute0, TileAttribute1, TilePattern0, TilePattern1)
44 | variable attr_offset, pattern_offset : integer;
45 | begin
46 | attr_offset := 7 - to_integer(FineXScrolling);
47 | pattern_offset := 15 - to_integer(FineXScrolling);
48 |
49 | TileColor <= TileAttribute1(attr_offset) & TileAttribute0(attr_offset) &
50 | TilePattern1(pattern_offset) & TilePattern0(pattern_offset);
51 | end process;
52 |
53 | SHIFT_REGS : process(CE, clk)
54 | begin
55 | if rising_edge(clk) and CE = '1' then
56 | if EnableRendering = '1' then
57 | TilePattern0 <= TilePattern0(14 downto 0) & "-";
58 | TilePattern1 <= TilePattern1(14 downto 0) & "-";
59 |
60 | if Fine_HPOS mod 8 = 0 then
61 | TilePattern0(7 downto 0) <= NextPattern0;
62 | TilePattern1(7 downto 0) <= NextPattern1;
63 | AttributeLatch0 <= NextAttribute0;
64 | AttributeLatch1 <= NextAttribute1;
65 | end if;
66 |
67 | TileAttribute0 <= TileAttribute0(6 downto 0) & AttributeLatch0;
68 | TileAttribute1 <= TileAttribute1(6 downto 0) & AttributeLatch1;
69 | end if;
70 | end if;
71 | end process;
72 |
73 | PREFETCH : process(CE, clk, rstn)
74 | -- This register is sometimes called PAR (Picture Address Register),
75 | -- in the 2C02 related patent document
76 | variable NextTileName : unsigned(7 downto 0);
77 |
78 | -- Helper variables
79 | variable attr_offset : unsigned(2 downto 0);
80 | variable aoi : integer range 0 to 7;
81 | begin
82 | if rstn = '0' then
83 | VRAM_Address <= (others => '0');
84 | elsif rising_edge(clk) and CE = '1' then
85 | case Fine_HPOS mod 8 is
86 | when 0 =>
87 | VRAM_Address <= "10" & Loopy_v(11 downto 0);
88 | when 1 =>
89 | NextTileName := unsigned(VRAM_Data);
90 | when 2 =>
91 | VRAM_Address <= "10" & Loopy_v(11 downto 10) & "1111" & Loopy_v(9 downto 7) & Loopy_v(4 downto 2);
92 | when 3 =>
93 | attr_offset := Loopy_v(6) & Loopy_v(1) & "0";
94 | aoi := to_integer(attr_offset);
95 | NextAttribute0 <= VRAM_Data(aoi);
96 | NextAttribute1 <= VRAM_Data(aoi + 1);
97 | when 4 =>
98 | VRAM_Address <= "0" & PatternTableAddressOffset & NextTileName & "0" & Loopy_v(14 downto 12);
99 | when 5 =>
100 | NextPattern0 <= VRAM_Data;
101 | when 6 =>
102 | VRAM_Address <= "0" & PatternTableAddressOffset & NextTileName & "1" & Loopy_v(14 downto 12);
103 | when 7 =>
104 | NextPattern1 <= VRAM_Data;
105 | -- TilePattern1 <= VRAM_Data & TilePattern1(15 downto 8);
106 | -- TilePattern0 <= NextTilePattern0 & TilePattern0(15 downto 8);
107 | -- TileAttribute <= NextTileAttribute;
108 | when others =>
109 | end case;
110 | end if;
111 | end process;
112 |
113 | end Behavioral;
114 |
115 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | NESFPGA
2 | =======
3 |
4 | This project implements a simplified version of the
5 | Nintendo Entertainment System on an FPGA, that can execute Super Mario Bros.
6 |
7 | Getting Started
8 | ---------------
9 |
10 | I used the Digilent Genesys FPGA board, which is currently the only board this
11 | project works on.
12 | Sadly, you will need a version of Xilinx ISE software to synthesize this project.
13 | I used version 14.5, the free Webpack Edition should be fine.
14 |
15 | Create a new project, and first include the top module Genesys_NES.vhd,
16 | to see what other files are missing. Don't forget the constraints file nes_top.ucf.
17 | Then add every .vhd and .v file, except the TestBenches, and CartridgeROM*.vhd
18 |
19 | ### Cartridge
20 |
21 | CartridgeROM.vhd reads the ROM data from .dat files in the directory roms/.
22 | The default filenames are 'roms/smb_chr.dat' and 'roms/smb_prg.dat'.
23 | These files are already shipped with the project, but you can generate them
24 | with tools/romconv.py. Beware, this project only supports 32 KiB ROMs.
25 |
26 | XST should support the ROM inferrence style in CartridgeROM.vhd, but this is rather slow.
27 | For faster synthesis, use CartridgeROM_Coregen.vhd and include
28 | the appropriate .ngc files from the Coregen directory.
29 | This directory also includes the Coregen project files, and romconv.py can be
30 | modified to generate the necessary .coe files from .nes ROMs.
31 |
32 |
33 | ### MyHDL Part
34 |
35 | Since the APU is programmed in MyHDL, it needs to be converted to VHDL or Verilog
36 | code for synthesis. apu_convert.py does this for you, but you will need to install
37 | MyHDL (at least version 0.7).
38 | Alternatively, for synthesis you can just use the pregenerated file APU/APU_Main.v.
39 |
40 |
41 | Directory Overview
42 | ------------------
43 |
44 |
45 | NES_2A03 - The modified CPU from Dan Leach's NES-On-a-FPGA project
46 | PPU - Implementation of the NES 2C02
47 | APU - MyHDL code and testbench for the APU
48 |
49 | TestBenches - Various VHDL based testbenches
50 | tools - Tools for generating ROM helper files, and framebuffer viewer
51 | roms - ROM files in .nes and converted form
52 |
53 | HDMI - Chrontel CH7301C interface from xps_tft
54 | ac97 - The 3rd party AC97 module with my wrapper
55 |
56 | synaesthesia - First attempt at a ISE-independent build system... Ignore for now
57 |
58 |
59 | Flashing the FPGA board
60 | -----------------------
61 |
62 | To persistently save the bistream on the FPGA boards flash RAM, you
63 | need to use Xilinx iMPact. These are the settings i used to generate
64 | the MCS file:
65 |
66 | - Parallel BPI Single Boot
67 | - Virtex 5 / 32M
68 | - 16 bit width
69 | - 28F256P30 (not 512, the genesys schematic is lying)
70 |
71 |
72 | Testbenches
73 | -----------
74 |
75 | ### NES_Framebuffer_TB.vhd
76 |
77 | This is the testbench for simulating everything down from NES_Mainboard.
78 | But beware, you will need a fast and expensive simulator for this to be useful.
79 |
80 | It writes the framebuffer data to fbdump_top.out.
81 | The FBView tool in tools/fbview can be used to view this file.
82 | It includes a build script, but you will need a working gcc and the SDL
83 | library to compile it.
84 |
85 | The testbench also includes a primitive way for simulating controller pad inputs
86 | in the process CONTROLLER_INPUT. It uses 1 for not pressed and 0 for pressed.
87 | From left to right, the button mapping is "Right, Left, Down, Up, Start, Select, B, A"
88 |
89 | ### apu_tb.py
90 |
91 | Unlike the other testbenches, this is found in the APU directory.
92 | To use it, you will need Py65. Use these commands to get
93 | and configure python to find it:
94 |
95 | $ cd APU
96 | $ git clone https://github.com/mnaberez/py65
97 | $ export PYTHONPATH=py65
98 |
99 | To start simulating the first song of the SMB nsf, use this command:
100 |
101 | $ python apu_tb.py smb.nsf 0
102 |
103 | It will write the file smb-0.wav to the output directory.
104 |
105 | I recommend you to acquire a recent version of PyPy, and use it instead
106 | of standard CPython, as it speeds up the simulation by orders of magnitude
107 |
--------------------------------------------------------------------------------
/TestBenches/DummySound_TB.vhd:
--------------------------------------------------------------------------------
1 | LIBRARY ieee;
2 | USE ieee.std_logic_1164.ALL;
3 | USE ieee.numeric_std.ALL;
4 |
5 | use STD.textio.ALL;
6 |
7 | ENTITY DummySound_TB IS
8 | END DummySound_TB;
9 |
10 | ARCHITECTURE behavior OF DummySound_TB IS
11 |
12 | COMPONENT DummySound
13 | PORT(
14 | CLK : IN std_logic;
15 | CE : IN std_logic;
16 | RSTN : IN std_logic;
17 |
18 | PCM_4BIT_OUT : out signed(3 downto 0)
19 |
20 |
21 | -- VRAM_Address : OUT unsigned(13 downto 0);
22 | -- VRAM_Data : IN std_logic_vector(7 downto 0);
23 | );
24 | END COMPONENT;
25 |
26 |
27 | --Outputs
28 | signal DUMMY_PCM : unsigned(3 downto 0) := X"0";
29 |
30 | -- Clock period definitions
31 | constant AC97_CLK_period : time := 20.833333333333332 us; -- 48 kHz
32 | constant CLK_period : time := 46.560848137510206 ns; -- 21.477272 MhZ
33 |
34 | constant APU_clkdiv : integer := 2 * 12; -- Half CPU Clock
35 |
36 | signal AC97_CLK : std_logic := '0';
37 |
38 | signal CLK : std_logic := '0';
39 | signal APU_CE : std_logic := '0';
40 | signal RSTN : std_logic := '0';
41 |
42 | signal APU_CE_cnt : integer := 0;
43 |
44 | BEGIN
45 |
46 |
47 | APU_CE <= '1' when APU_CE_cnt = 0 else '0';
48 |
49 | CLK <= not CLK after CLK_period;
50 | AC97_CLK <= not AC97_CLK after AC97_CLK_period;
51 | RSTN <= '1' after 100 ns;
52 |
53 | process (clk)
54 | begin
55 | if rising_edge(clk) then
56 | if APU_CE_cnt = APU_clkdiv - 1 then
57 | APU_CE_cnt <= 0;
58 | else
59 | APU_CE_cnt <= APU_CE_cnt + 1;
60 | end if;
61 | end if;
62 | end process;
63 |
64 | -- uut: APU_Pulse PORT MAP (
65 | -- CLK => CLK,
66 | -- CE => CE,
67 | -- RSTN => RSTN
68 | -- );
69 | --
70 |
71 | APU_PULSE : process(CLK)
72 | variable note : unsigned(11 downto 0) := X"0FD";
73 | variable sequencer : std_logic_vector(7 downto 0) := "11110000";
74 | variable timer : integer := 0;
75 | begin
76 | if rising_edge(CLK) and APU_CE = '1' then
77 | if timer = 0 then
78 | timer := to_integer(note);
79 | sequencer := sequencer(6 downto 0) & sequencer(7);
80 | DUMMY_PCM <= X"0";
81 | if sequencer(0) = '1' then DUMMY_PCM <= X"F"; end if;
82 | else
83 | timer := timer - 1;
84 | end if;
85 | end if;
86 | end process;
87 |
88 |
89 | WRITE_OUTPUT_proc : process(AC97_CLK)
90 | type AudioFileType is file of character;
91 | file outfile : AudioFileType open WRITE_MODE is "audio.dump";
92 | variable num_samples : integer := 0;
93 | begin
94 | if rising_edge(AC97_CLK) then
95 | write(outfile, character'val(to_integer(DUMMY_PCM & "0000")));
96 | -- write(outfile, character'val(num_samples));
97 |
98 | num_samples := num_samples + 1;
99 |
100 | assert num_samples mod 48000 /= 0 report "Wrote 1000 ms" severity note;
101 |
102 |
103 | end if;
104 | end process;
105 |
106 | END;
107 |
--------------------------------------------------------------------------------
/TestBenches/NES_Framebuffer_TB.vhd:
--------------------------------------------------------------------------------
1 | library ieee;
2 | use ieee.std_logic_1164.all;
3 | use ieee.numeric_std.all;
4 | use STD.textio.all;
5 |
6 | entity NES_Framebuffer_TB is
7 | end entity;
8 |
9 | architecture arch of NES_Framebuffer_TB is
10 |
11 | component NES_Mainboard is
12 | port (
13 | clk : in std_logic; -- approximation to NES mainboard clock 21.47727
14 | rstn : in std_logic;
15 |
16 | -- Framebuffer output
17 | FB_Address : out unsigned(15 downto 0); -- linear index in 256x240 pixel framebuffer
18 | FB_Color : out std_logic_vector(5 downto 0); -- Palette index of current color
19 | FB_DE : out std_logic; -- True when PPU is writing to the framebuffer
20 |
21 | -- Controller input
22 | Controller_Strobe : out std_logic; -- Set shift register in controller with current buttons
23 | Controller1_Clock : out std_logic; -- Shift register by one bit on falling edge
24 | Controller2_Clock : out std_logic; -- Shift register by one bit on falling edge
25 |
26 | Controller1_Data0_N : in std_logic; -- Shift register highest bit
27 | Controller1_Data1_N : in std_logic; -- Not connected in standard controllers
28 | Controller1_Data2_N : in std_logic; -- Not connected in standard controllers
29 |
30 | Controller2_Data0_N : in std_logic; -- Shift register highest bit
31 | Controller2_Data1_N : in std_logic; -- Not connected in standard controllers
32 | Controller2_Data2_N : in std_logic; -- Not connected in standard controllers
33 |
34 | PinWithoutSemicolon : out std_logic
35 | );
36 | end component;
37 |
38 | signal NES_Clock : std_logic;
39 | signal NES_Reset : std_logic;
40 |
41 | signal FB_Address : unsigned(15 downto 0);
42 | signal FB_Color : std_logic_vector(5 downto 0);
43 | signal FB_DE : std_logic;
44 |
45 | signal Controller_Strobe : std_logic;
46 | signal Controller1_Clock : std_logic;
47 | signal Controller2_Clock : std_logic;
48 |
49 | signal Controller1_ShiftRegister : std_logic_vector(7 downto 0);
50 |
51 | constant fb_size : integer := 340 * 261 - 1;
52 | type fb_ram_type is array(fb_size downto 0) of std_logic_vector(5 downto 0);
53 | type FramebufferFileType is file of fb_ram_type;
54 |
55 | signal fb_ram : fb_ram_type := (others => "101010");
56 |
57 | signal FrameCount : integer := 0;
58 |
59 | constant CLK_period : time := 1 us / 21.47727;
60 |
61 | begin
62 |
63 | uut : NES_Mainboard port map (
64 | clk => NES_Clock,
65 | rstn => NES_Reset,
66 |
67 | FB_Address => FB_Address,
68 | FB_Color => FB_Color,
69 | FB_DE => FB_DE,
70 |
71 | Controller_Strobe => Controller_Strobe,
72 | Controller1_Clock => Controller1_Clock,
73 | Controller2_Clock => Controller2_Clock,
74 |
75 | Controller1_Data0_N => Controller1_ShiftRegister(0),
76 | Controller1_Data1_N => '0',
77 | Controller1_Data2_N => '0',
78 | Controller2_Data0_N => '0',
79 | Controller2_Data1_N => '0',
80 | Controller2_Data2_N => '0',
81 |
82 | PinWithoutSemicolon => open
83 | );
84 |
85 |
86 | -- Clock process definitions
87 | CLK_process :process
88 | begin
89 | NES_Clock <= '0';
90 | wait for CLK_period/2;
91 | NES_Clock <= '1';
92 | wait for CLK_period/2;
93 | end process;
94 |
95 |
96 | -- Stimulus process
97 | stim_proc: process
98 | begin
99 | NES_Reset <= '0';
100 | wait for 1 ms;
101 | NES_Reset <= '1';
102 | wait;
103 | end process;
104 |
105 | CONTROLLER_INPUT: process (Controller_Strobe, Controller1_Clock)
106 | begin
107 | if falling_edge(Controller_Strobe) then
108 | case FrameCount is
109 | when 10 =>
110 | -- Controller1_ShiftRegister <= "00000000";
111 | Controller1_ShiftRegister <= "11111011";
112 | when 13 =>
113 | -- Controller1_ShiftRegister <= "00000000";
114 | Controller1_ShiftRegister <= "11111011";
115 | when 16 =>
116 | -- Controller1_ShiftRegister <= "00000000";
117 | Controller1_ShiftRegister <= "11111110";
118 | when 35 =>
119 | Controller1_ShiftRegister <= "11111110";
120 | when 52 =>
121 | Controller1_ShiftRegister <= "11111110";
122 | when 70 =>
123 | Controller1_ShiftRegister <= "11111110";
124 | when others =>
125 | Controller1_ShiftRegister <= "11111111";
126 | end case;
127 | elsif Controller_Strobe = '0' and rising_edge(Controller1_Clock) then
128 | Controller1_ShiftRegister <= "1" & Controller1_ShiftRegister(7 downto 1);
129 | end if;
130 | end process;
131 |
132 | FB_WRITE_proc : process (NES_Clock)
133 | variable v, h : integer;
134 | begin
135 | if rising_edge(Nes_Clock) then
136 | if FB_DE = '1' then
137 | -- fb_ram stores the full 340x261 range, but the PPU only outputs
138 | -- the visible range 256x240. For this reason, we recompute the address
139 | -- here:
140 |
141 | v := to_integer(FB_Address / 256);
142 | h := to_integer(FB_Address mod 256) + 42;
143 |
144 | fb_ram(v * 340 + h) <= FB_Color;
145 | end if;
146 | end if;
147 | end process;
148 |
149 | FB_DUMP_proc : process (FB_Address, FB_DE)
150 | file my_output : FramebufferFileType open WRITE_MODE is "fbdump_top.out";
151 | variable my_line : LINE;
152 | variable my_output_line : LINE;
153 | begin
154 | if rising_edge(FB_DE) and FB_Address = X"0000" then
155 | write(my_line, string'("writing file"));
156 | writeline(output, my_line);
157 | write(my_output, fb_ram);
158 | FrameCount <= FrameCount + 1;
159 | end if;
160 | end process;
161 |
162 | end architecture arch;
--------------------------------------------------------------------------------
/TestBenches/PPU_tb.vhd:
--------------------------------------------------------------------------------
1 |
2 | LIBRARY ieee;
3 | USE ieee.std_logic_1164.ALL;
4 | USE ieee.numeric_std.ALL;
5 |
6 |
7 | use IEEE.std_logic_textio.all;
8 | use STD.textio.all;
9 |
10 | ENTITY PPU_tb IS
11 | END PPU_tb;
12 |
13 | ARCHITECTURE behavior OF PPU_tb IS
14 |
15 | component NES_2C02 is
16 | port (
17 | clk : in std_logic; -- input clock, 5,37 MHz.
18 | rstn : in std_logic;
19 |
20 | -- CPU Bus
21 | ChipSelect_n : in std_logic;
22 | ReadWrite : in std_logic; -- Write to PPU on 0
23 | Address : in std_logic_vector(2 downto 0);
24 | Data_in : in std_logic_vector(7 downto 0);
25 | Data_out : out std_logic_vector(7 downto 0);
26 |
27 | -- VRAM/VROM bus
28 | CHR_Address : out unsigned(13 downto 0);
29 | CHR_Data : in std_logic_vector(7 downto 0);
30 |
31 | VBlank_n : out std_logic; -- Tied to the CPU's Non-Maskable Interrupt (NMI)
32 |
33 | -- Framebuffer output
34 | FB_Address : out std_logic_vector(15 downto 0); -- linear index in 256x240 pixel framebuffer
35 | FB_Color : out std_logic_vector(5 downto 0); -- Palette index of current color
36 | FB_DE : out std_logic -- True when PPU is writing to the framebuffer
37 | );
38 | end component;
39 |
40 |
41 | component CartridgeROM is
42 | port (
43 | clk : in std_logic; -- input clock, xx MHz.
44 | rstn : in std_logic;
45 |
46 | PRG_Address : in std_logic_vector(14 downto 0);
47 | PRG_Data : out std_logic_vector(7 downto 0);
48 |
49 | CHR_Address : in unsigned(13 downto 0);
50 | CHR_Data : out std_logic_vector(7 downto 0)
51 | );
52 | end component;
53 |
54 | --Inputs
55 | signal clk : std_logic := '0';
56 | signal rstn : std_logic := '0';
57 | signal ChipSelect_n : std_logic := '0';
58 | signal ReadWrite : std_logic := '0';
59 | signal Address : std_logic_vector(2 downto 0) := (others => '0');
60 | signal Data_in : std_logic_vector(7 downto 0) := (others => '0');
61 | signal CHR_Data : std_logic_vector(7 downto 0) := (others => '0');
62 |
63 | --Outputs
64 | signal Data_out : std_logic_vector(7 downto 0);
65 | signal CHR_Address : unsigned(13 downto 0);
66 | signal VBlank_n : std_logic;
67 | signal FB_Address : std_logic_vector(15 downto 0);
68 | signal FB_Color : std_logic_vector(5 downto 0);
69 | signal FB_DE : std_logic;
70 |
71 | type fb_ram_type is array(65535 downto 0) of std_logic_vector(5 downto 0);
72 |
73 | signal fb_ram : fb_ram_type;
74 |
75 | -- Clock period definitions
76 | constant clk_period : time := 10 ns;
77 |
78 | type FramebufferFileType is file of fb_ram_type;
79 |
80 | BEGIN
81 |
82 | -- Instantiate the Unit Under Test (UUT)
83 | uut: NES_2C02 PORT MAP (
84 | clk => clk,
85 | rstn => rstn,
86 | ChipSelect_n => ChipSelect_n,
87 | ReadWrite => ReadWrite,
88 | Address => Address,
89 | Data_in => Data_in,
90 | Data_out => Data_out,
91 | CHR_Address => CHR_Address,
92 | CHR_Data => CHR_Data,
93 | VBlank_n => VBlank_n,
94 | FB_Address => FB_Address,
95 | FB_Color => FB_Color,
96 | FB_DE => FB_DE
97 | );
98 |
99 | mem: CartridgeROM
100 | port map (
101 | clk => clk,
102 | rstn => rstn,
103 | PRG_Address => (others => '0'),
104 | PRG_Data => open,
105 | CHR_Address => CHR_Address,
106 | CHR_Data => CHR_Data
107 | );
108 |
109 | process (clk)
110 | begin
111 | if rising_edge(clk) then
112 | if FB_DE = '1' then
113 | fb_ram(to_integer(unsigned(FB_Address))) <= FB_Color;
114 | end if;
115 | end if;
116 | end process;
117 |
118 | FB_DUMP: process (VBlank_n)
119 | file my_output : FramebufferFileType open WRITE_MODE is "file_io.out";
120 | -- above declaration should be in architecture declarations for multiple
121 | variable my_line : LINE;
122 | variable my_output_line : LINE;
123 | begin
124 | if falling_edge(VBlank_n) then
125 | write(my_line, string'("writing file"));
126 | writeline(output, my_line);
127 | write(my_output, fb_ram);
128 | -- writeline(my_output, my_output_line);
129 | end if;
130 | end process;
131 |
132 | -- Clock process definitions
133 | clk_process :process
134 | begin
135 | clk <= '0';
136 | wait for clk_period/2;
137 | clk <= '1';
138 | wait for clk_period/2;
139 | end process;
140 |
141 |
142 | -- Stimulus process
143 | stim_proc: process
144 | begin
145 | rstn <= '0';
146 | wait for 100 ns;
147 | rstn <= '1';
148 |
149 | wait for clk_period*10;
150 |
151 | -- Enable VBlank Interrupt in Register $2000
152 | Address <= "000";
153 | Data_in <= "10000000";
154 | ReadWrite <= '0';
155 |
156 | wait for clk_period*3;
157 |
158 | ChipSelect_n <= '1';
159 |
160 | wait for clk_period*3;
161 | ReadWrite <= '1';
162 | wait;
163 | end process;
164 |
165 | END;
166 |
--------------------------------------------------------------------------------
/TestBenches/SpriteSelector_TB.vhd:
--------------------------------------------------------------------------------
1 | LIBRARY ieee;
2 | USE ieee.std_logic_1164.ALL;
3 | USE ieee.numeric_std.ALL;
4 |
5 | use STD.textio.ALL;
6 |
7 | use work.NES_Pack.all;
8 |
9 | ENTITY SpriteSelector_TB IS
10 | END SpriteSelector_TB;
11 |
12 | ARCHITECTURE behavior OF SpriteSelector_TB IS
13 |
14 | COMPONENT SpriteSelector
15 | PORT(
16 | CLK : IN std_logic;
17 | CE : IN std_logic;
18 | RSTN : IN std_logic;
19 | HPOS : IN integer;
20 | VPOS : IN integer;
21 | PatternTableAddressOffset : IN std_logic;
22 | SpriteColor : OUT unsigned(3 downto 0);
23 | SpriteForegroundPriority : OUT std_logic;
24 | SpriteIsPrimary : OUT std_logic;
25 | SpriteOverflowFlag : OUT std_logic;
26 | VRAM_Address : OUT unsigned(13 downto 0);
27 | VRAM_Data : IN std_logic_vector(7 downto 0);
28 | SpriteRAM_Address : IN unsigned(7 downto 0);
29 | SpriteRAM_Data_in : IN std_logic_vector(7 downto 0);
30 | SpriteRAM_Data_out : OUT std_logic_vector(7 downto 0);
31 | SpriteRAM_WriteEnable : IN std_logic
32 | );
33 | END COMPONENT;
34 |
35 |
36 | --Inputs
37 | signal PatternTableAddressOffset : std_logic := '0';
38 | signal VRAM_Data : std_logic_vector(7 downto 0) := (others => '0');
39 | signal SpriteRAM_Address : unsigned(7 downto 0) := (others => '0');
40 | signal SpriteRAM_Data_in : std_logic_vector(7 downto 0) := (others => '0');
41 | signal SpriteRAM_WriteEnable : std_logic := '0';
42 |
43 | --Outputs
44 | signal SpriteColor : unsigned(3 downto 0);
45 | signal SpriteForegroundPriority : std_logic;
46 | signal SpriteIsPrimary : std_logic;
47 | signal SpriteOverflowFlag : std_logic;
48 | signal VRAM_Address : unsigned(13 downto 0);
49 | signal SpriteRAM_Data_out : std_logic_vector(7 downto 0);
50 |
51 | -- Clock period definitions
52 | constant CLK_period : time := 10 ns;
53 |
54 | constant fb_size : integer := 340 * 261 - 1;
55 | type fb_ram_type is array(fb_size downto 0) of std_logic_vector(5 downto 0);
56 | type FramebufferFileType is file of fb_ram_type;
57 | signal fb_ram : fb_ram_type := (others => "101010");
58 |
59 |
60 | signal CLK : std_logic := '0';
61 | signal CE : std_logic := '0';
62 | signal RSTN : std_logic := '0';
63 |
64 | signal HPOS : integer range -42 to 298; -- negative values mark the HBlank period
65 | signal VPOS : integer range 0 to 261; -- 240 to 261 is the VBlank period
66 |
67 | signal CE_cnt : integer := 0;
68 |
69 | BEGIN
70 |
71 | CE <= '1' when CE_cnt = 0 else '0';
72 |
73 | CLK_proc : process
74 | begin
75 | CLK <= '0';
76 | wait for CLK_period / 2;
77 | CLK <= '1';
78 | wait for CLK_period / 2;
79 | end process;
80 |
81 | process (clk)
82 | begin
83 | if rising_edge(clk) then
84 | if CE_cnt = 3 then
85 | CE_cnt <= 0;
86 | else
87 | CE_cnt <= CE_cnt + 1;
88 | end if;
89 | end if;
90 | end process;
91 |
92 | -- Instantiate the Unit Under Test (UUT)
93 | uut: SpriteSelector PORT MAP (
94 | CLK => CLK,
95 | CE => CE,
96 | RSTN => RSTN,
97 | HPOS => HPOS,
98 | VPOS => VPOS,
99 | PatternTableAddressOffset => PatternTableAddressOffset,
100 | SpriteColor => SpriteColor,
101 | SpriteForegroundPriority => SpriteForegroundPriority,
102 | SpriteIsPrimary => SpriteIsPrimary,
103 | SpriteOverflowFlag => SpriteOverflowFlag,
104 | VRAM_Address => VRAM_Address,
105 | VRAM_Data => VRAM_Data,
106 | SpriteRAM_Address => SpriteRAM_Address,
107 | SpriteRAM_Data_in => SpriteRAM_Data_in,
108 | SpriteRAM_Data_out => SpriteRAM_Data_out,
109 | SpriteRAM_WriteEnable => SpriteRAM_WriteEnable
110 | );
111 |
112 | Cartridge : CartridgeROM
113 | port map (
114 | clk => clk,
115 | rstn => rstn,
116 | PRG_Address => (others => '0'),
117 | PRG_Data => open,
118 | CHR_Address => VRAM_Address,
119 | CHR_Data => VRAM_Data
120 | );
121 |
122 | process (clk)
123 | begin
124 | if rising_edge(clk) and CE = '1' then
125 | if RSTN = '0' then
126 | HPOS <= -42;
127 | VPOS <= 0;
128 | else
129 | if HPOS = 297 then
130 | HPOS <= -42;
131 | if VPOS = 260 then
132 | VPOS <= 0;
133 | else
134 | VPOS <= VPOS + 1;
135 | end if;
136 | else
137 | HPOS <= HPOS + 1;
138 | end if;
139 | end if;
140 | end if;
141 | end process;
142 |
143 | -- Stimulus process
144 | stim_proc: process
145 | begin
146 | SpriteRAM_Address <= (others => '0');
147 | SpriteRAM_Data_in <= (others => '0');
148 | SpriteRAM_WriteEnable <= '0';
149 |
150 | RSTN <= '0';
151 | wait for 100 ns;
152 |
153 | wait for CLK_period*10;
154 |
155 |
156 | RSTN <= '1';
157 |
158 | SpriteRAM_WriteEnable <= '1';
159 |
160 | for i in 0 to 127 loop
161 | SpriteRAM_Address <= to_unsigned(i, 8);
162 |
163 | case i mod 4 is
164 | when 0 =>
165 | SpriteRAM_Data_in <= std_logic_vector(to_unsigned(10 + (i / 8) * 8, 8));
166 | when 1 =>
167 | SpriteRAM_Data_in <= std_logic_vector(to_unsigned(i / 4, 8));
168 | -- SpriteRAM_Data_in <= X"00";
169 | when 2 =>
170 | SpriteRAM_Data_in <= "11111111";
171 | when 3 =>
172 | SpriteRAM_Data_in <= std_logic_vector(to_unsigned(30 + ((i / 4 + 1) mod 2) * 8 , 8));
173 | when others =>
174 | end case;
175 |
176 | -- Also write the primary sprite used by Super Mario Brothers for synchronization purposes
177 | if i = 0 then
178 | SpriteRAM_Data_in <= X"18";
179 | elsif i = 1 then
180 | SpriteRAM_Data_in <= X"FF";
181 | elsif i = 2 then
182 | SpriteRAM_Data_in <= X"23";
183 | elsif i = 3 then
184 | SpriteRAM_Data_in <= X"58";
185 | end if;
186 |
187 | wait for CLK_period * 4;
188 | end loop;
189 | SpriteRAM_WriteEnable <= '0';
190 |
191 | wait;
192 | end process;
193 |
194 | FB_WRITE_proc : process (clk)
195 | variable fbaddr : integer;
196 | begin
197 | if rising_edge(clk) and CE = '1' then
198 | fbaddr := (HPOS + 42) + VPOS * 340;
199 | fb_ram(fbaddr) <= "00" & std_logic_vector(SpriteColor);
200 |
201 | -- Draw a border for the visible part of the screen
202 | if HPOS = -1 or HPOS = 256 or VPOS = 240 then
203 | fb_ram(fbaddr) <= "111010";
204 | end if;
205 | end if;
206 | end process;
207 |
208 | FB_DUMP_proc : process (clk)
209 | file my_output : FramebufferFileType open WRITE_MODE is "spritetb.out";
210 | variable my_line : LINE;
211 | variable my_output_line : LINE;
212 | begin
213 | if rising_edge(clk) and CE = '1' and HPOS = 297 and VPOS = 260 then
214 | write(my_line, string'("writing file"));
215 | writeline(output, my_line);
216 | write(my_output, fb_ram);
217 | end if;
218 | end process;
219 |
220 | END;
221 |
--------------------------------------------------------------------------------
/TestBenches/nes_top_tb.vhd:
--------------------------------------------------------------------------------
1 | LIBRARY ieee;
2 | USE ieee.std_logic_1164.ALL;
3 |
4 | -- Uncomment the following library declaration if using
5 | -- arithmetic functions with Signed or Unsigned values
6 | --USE ieee.numeric_std.ALL;
7 |
8 | ENTITY nes_top_tb IS
9 | END nes_top_tb;
10 |
11 | ARCHITECTURE behavior OF nes_top_tb IS
12 |
13 | -- Component Declaration for the Unit Under Test (UUT)
14 |
15 | COMPONENT nes_top
16 | PORT(
17 | CLK : IN std_logic;
18 | RSTN : IN std_logic;
19 | HDMIHSync : OUT std_logic;
20 | HDMIVSync : OUT std_logic;
21 | HDMIDE : OUT std_logic;
22 | HDMICLKP : OUT std_logic;
23 | HDMICLKN : OUT std_logic;
24 | HDMID : OUT std_logic_vector(11 downto 0);
25 | HDMISCL : INOUT std_logic;
26 | HDMISDA : INOUT std_logic;
27 | HDMIRSTN : OUT std_logic;
28 | -- LED : OUT std_logic_vector(0 to 7);
29 | BTN : IN std_logic_vector(0 to 6)
30 | );
31 | END COMPONENT;
32 |
33 |
34 | --Inputs
35 | signal CLK : std_logic := '0';
36 | signal RSTN : std_logic := '0';
37 | signal BTN : std_logic_vector(0 to 6) := (others => '0');
38 |
39 | --BiDirs
40 | signal HDMISCL : std_logic;
41 | signal HDMISDA : std_logic;
42 |
43 | --Outputs
44 | signal HDMIHSync : std_logic;
45 | signal HDMIVSync : std_logic;
46 | signal HDMIDE : std_logic;
47 | signal HDMICLKP : std_logic;
48 | signal HDMICLKN : std_logic;
49 | signal HDMID : std_logic_vector(11 downto 0);
50 | signal HDMIRSTN : std_logic;
51 | signal LED : std_logic_vector(0 to 7);
52 |
53 | -- Clock period definitions
54 | constant CLK_period : time := 10 ns;
55 |
56 | BEGIN
57 |
58 | -- Instantiate the Unit Under Test (UUT)
59 | uut: nes_top PORT MAP (
60 | CLK => CLK,
61 | RSTN => RSTN,
62 | HDMIHSync => HDMIHSync,
63 | HDMIVSync => HDMIVSync,
64 | HDMIDE => HDMIDE,
65 | HDMICLKP => HDMICLKP,
66 | HDMICLKN => HDMICLKN,
67 | HDMID => HDMID,
68 | HDMISCL => HDMISCL,
69 | HDMISDA => HDMISDA,
70 | HDMIRSTN => HDMIRSTN,
71 | -- LED => LED,
72 | BTN => BTN
73 | );
74 |
75 | -- Clock process definitions
76 | CLK_process :process
77 | begin
78 | CLK <= '0';
79 | wait for CLK_period/2;
80 | CLK <= '1';
81 | wait for CLK_period/2;
82 | end process;
83 |
84 |
85 | -- Stimulus process
86 | stim_proc: process
87 | begin
88 | RSTN <= '0';
89 | -- hold reset state for 100 ns.
90 | wait for 100 ns;
91 |
92 | wait for CLK_period*10;
93 |
94 | RSTN <= '1';
95 |
96 | -- insert stimulus here
97 |
98 | wait;
99 | end process;
100 |
101 | END;
102 |
--------------------------------------------------------------------------------
/ac97/Talkthrough_Parts.vhd:
--------------------------------------------------------------------------------
1 | --//////////Package file for AC97 Talkthrough //////////////////////////--
2 | -- ***********************************************************************
3 | -- FileName: Talkthrough_Parts.vhd
4 | -- FPGA: Xilinx Spartan 6
5 | -- IDE: Xilinx ISE 13.1
6 | --
7 | -- HDL IS PROVIDED "AS IS." DIGI-KEY EXPRESSLY DISCLAIMS ANY
8 | -- WARRANTY OF ANY KIND, WHETHER EXPRESS OR IMPLIED, INCLUDING BUT NOT
9 | -- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
10 | -- PARTICULAR PURPOSE, OR NON-INFRINGEMENT. IN NO EVENT SHALL DIGI-KEY
11 | -- BE LIABLE FOR ANY INCIDENTAL, SPECIAL, INDIRECT OR CONSEQUENTIAL
12 | -- DAMAGES, LOST PROFITS OR LOST DATA, HARM TO YOUR EQUIPMENT, COST OF
13 | -- PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY OR SERVICES, ANY CLAIMS
14 | -- BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
15 | -- ANY CLAIMS FOR INDEMNITY OR CONTRIBUTION, OR OTHER SIMILAR COSTS.
16 | -- DIGI-KEY ALSO DISCLAIMS ANY LIABILITY FOR PATENT OR COPYRIGHT
17 | -- INFRINGEMENT.
18 | --
19 | -- Version History
20 | -- Version 1.0 12/06/2011 Tony Storey
21 | -- Initial Public Release
22 |
23 |
24 | library IEEE;
25 | use IEEE.STD_LOGIC_1164.all;
26 | use IEEE.numeric_std.all;
27 |
28 |
29 |
30 | package Spar6_Parts is
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | --/////////////////////// AC97 Driver //////////////////////////////////--
39 |
40 |
41 |
42 | component ac97
43 | port (
44 | n_reset : in std_logic;
45 | clk : in std_logic;
46 | -- ac97 interface signals
47 | ac97_sdata_out : out std_logic; -- ac97 output to SDATA_IN
48 | ac97_sdata_in : in std_logic; -- ac97 input from SDATA_OUT
49 | ac97_sync : out std_logic; -- SYNC signal to ac97
50 | ac97_bitclk : in std_logic; -- 12.288 MHz clock from ac97
51 | ac97_n_reset : out std_logic; -- ac97 reset for initialization [active low]
52 | ac97_ready_sig : out std_logic; -- pulse for one cycle
53 | L_out : in std_logic_vector(17 downto 0); -- lt chan output from ADC
54 | R_out : in std_logic_vector(17 downto 0); -- rt chan output from ADC
55 | L_in : out std_logic_vector(17 downto 0); -- lt chan input to DAC
56 | R_in : out std_logic_vector(17 downto 0); -- rt chan input to DAC
57 | latching_cmd : in std_logic;
58 | cmd_addr : in std_logic_vector(7 downto 0); -- cmd address coming in from ac97cmd state machine
59 | cmd_data : in std_logic_vector(15 downto 0) -- cmd data coming in from ac97cmd state machine
60 | );
61 | end component;
62 |
63 |
64 | --/////////////// STATE MACHINE TO CONFIGURE THE AC97 ///////////////////////////--
65 |
66 | component ac97cmd
67 | port (
68 | clk : in std_logic;
69 | ready : in std_logic;
70 | cmd_addr : out std_logic_vector(7 downto 0);
71 | cmd_data : out std_logic_vector(15 downto 0);
72 | latching_cmd : out std_logic;
73 | volume : in std_logic_vector(4 downto 0);
74 | source : in std_logic_vector(2 downto 0)
75 | );
76 | end component;
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 | end Spar6_Parts;
86 |
87 |
88 |
89 |
90 | -------------------------------------------------------------------------------------
91 | --//////////////////////////// COMPONENTS /////////////////////////////////////////--
92 | -------------------------------------------------------------------------------------
93 |
94 |
95 |
96 | --/////////////////////// AC97 CONTROLLER //////////////////////////////////--
97 |
98 |
99 |
100 |
101 | library ieee;
102 | use ieee.std_logic_1164.all;
103 | use ieee.numeric_std.all;
104 |
105 |
106 | entity ac97 is
107 | port (
108 | n_reset : in std_logic;
109 | clk : in std_logic;
110 | -- ac97 interface signals
111 | ac97_sdata_out : out std_logic; -- ac97 output to SDATA_IN
112 | ac97_sdata_in : in std_logic; -- ac97 input from SDATA_OUT
113 | ac97_sync : out std_logic; -- SYNC signal to ac97
114 | ac97_bitclk : in std_logic; -- 12.288 MHz clock from ac97
115 | ac97_n_reset : out std_logic; -- ac97 reset for initialization [active low]
116 | ac97_ready_sig : out std_logic; -- pulse for one cycle
117 | L_out : in std_logic_vector(17 downto 0); -- lt chan output from ADC
118 | R_out : in std_logic_vector(17 downto 0); -- rt chan output from ADC
119 | L_in : out std_logic_vector(17 downto 0); -- lt chan input to DAC
120 | R_in : out std_logic_vector(17 downto 0); -- rt chan input to DAC
121 | latching_cmd : in std_logic;
122 | cmd_addr : in std_logic_vector(7 downto 0); -- cmd address coming in from ac97cmd state machine
123 | cmd_data : in std_logic_vector(15 downto 0)); -- cmd data coming in from ac97cmd state machine
124 | end ac97;
125 |
126 |
127 | architecture arch of ac97 is
128 |
129 |
130 | signal Q1, Q2 : std_logic; -- signals to deliver one cycle pulse at specified time
131 | signal bit_count : std_logic_vector(7 downto 0); -- counter for aligning slots
132 | signal rst_counter : integer range 0 to 4097; -- counter to set ac97_reset high for ac97 init
133 |
134 | signal latch_cmd_addr : std_logic_vector(19 downto 0); -- signals to latch in registers and commands
135 | signal latch_cmd_data : std_logic_vector(19 downto 0);
136 |
137 | signal latch_left_data : std_logic_vector(19 downto 0);
138 | signal latch_right_data : std_logic_vector(19 downto 0);
139 |
140 | signal left_data : std_logic_vector(19 downto 0);
141 | signal right_data : std_logic_vector(19 downto 0);
142 | signal left_in_data : std_logic_vector(19 downto 0);
143 | signal right_in_data : std_logic_vector(19 downto 0);
144 |
145 |
146 | begin
147 |
148 | -- concat for 18 bit usage can concat further for 16 bit use
149 | -- by using <& "0000"> and
150 | -------------------------------------------------------------------------------------
151 | left_data <= L_out & "00";
152 | right_data <= R_out & "00";
153 |
154 | L_in <= left_in_data(19 downto 2);
155 | R_in <= right_in_data(19 downto 2);
156 |
157 |
158 | -- Delay for ac97_reset signal, clk = 100MHz
159 | -- delay 10ns * 37.89 us for active low reset on init
160 | --------------------------------------------------------------------------------------
161 | process (clk, n_reset)
162 | begin
163 | if (clk'event and clk = '1') then
164 | if n_reset = '0' then
165 | rst_counter <= 0;
166 | ac97_n_reset <= '0';
167 | elsif rst_counter = 3789 then
168 | ac97_n_reset <= '1';
169 | rst_counter <= 0;
170 | else
171 | rst_counter <= rst_counter + 1;
172 | end if;
173 | end if;
174 | end process;
175 |
176 |
177 | -- This process generates a single clkcycle pulse
178 | -- to get configuration data from the ac97cmd FSM
179 | -- and lets the user know when a sample is ready
180 | ---------------------------------------------------------------------------------------
181 | process (clk, n_reset, bit_count)
182 | begin
183 | if(clk'event and clk = '1') then
184 | Q2 <= Q1;
185 | if(bit_count = "00000000") then
186 | Q1 <= '0';
187 | Q2 <= '0';
188 | elsif(bit_count >= "10000001") then
189 | Q1 <= '1';
190 | end if;
191 | ac97_ready_sig <= Q1 and not Q2;
192 | end if;
193 | end process;
194 |
195 |
196 | -- ac97-link frame is 256 cycles
197 | -- [slot0], [slot1], [slot2], [slot3], [slot4], [slot5] ... [slot9], [slot10], [slot11], [slot12]
198 | -- slot 0 [tag phase] is 16 cycles slot1:12 are 20 cycles so 16 + 12 * 20 = 256 cycles
199 | -- ac97 link output frame [frame going out]
200 | ---------------------------------------------------------------------------------------
201 | process (n_reset, bit_count, ac97_bitclk)
202 | begin
203 |
204 | if(n_reset = '0') then -- active low reset
205 | bit_count <= "00000000"; -- starts bit count at 0
206 | elsif (ac97_bitclk'event and ac97_bitclk = '1') then -- rising edge of ac97_bitclk
207 |
208 | if bit_count = "11111111" then -- Generate sync signal for ac97
209 | ac97_sync <= '1'; -- at bitcnt = 255
210 |
211 | end if;
212 |
213 | if bit_count = "00001111" then -- at bitcnt = 15
214 | ac97_sync <= '0';
215 | end if;
216 |
217 |
218 | -- At the end of each frame the user data is latched in
219 | if bit_count = "11111111" then
220 | latch_cmd_addr <= cmd_addr & "000000000000";
221 | latch_cmd_data <= cmd_data & "0000";
222 | latch_left_data <= left_data;
223 | latch_right_data <= right_data;
224 | end if;
225 | -- Tag phase
226 | if (bit_count >= "00000000") and (bit_count <= "00001111") then -- bit count 0 to 15
227 | -- Slot 0 : Tag Phase
228 | case bit_count is -- Can create input signals to verify on tag phase
229 | when "00000000" => ac97_sdata_out <= '1'; -- AC Link Interface ready
230 | when "00000001" => ac97_sdata_out <= latching_cmd; -- Vaild Status Adress or Slot request
231 | when "00000010" => ac97_sdata_out <= '1'; -- Valid Status data
232 | when "00000011" => ac97_sdata_out <= '1'; -- Valid PCM Data (Left ADC)
233 | when "00000100" => ac97_sdata_out <= '1'; -- Valid PCM Data (Right ADC)
234 | when others => ac97_sdata_out <= '0';
235 | end case;
236 | -- starting at slot 1 add 20 bit counts each time
237 | elsif (bit_count >= "00010000") and (bit_count <= "00100011") then -- bit count 16 to 35
238 | -- Slot 1 : Command address (8-bits, left justified)
239 |
240 | if latching_cmd = '1' then
241 | ac97_sdata_out <= latch_cmd_addr(35 - to_integer(unsigned(bit_count)));
242 | else
243 | ac97_sdata_out <= '0';
244 | end if;
245 |
246 | elsif (bit_count >= "00100100") and (bit_count <= "00110111") then -- bit count 36 to 55
247 | -- Slot 2 : Command data (16-bits, left justified)
248 |
249 | if latching_cmd = '1' then
250 | ac97_sdata_out <= latch_cmd_data(55 - to_integer(unsigned(bit_count)));
251 | else
252 | ac97_sdata_out <= '0';
253 | end if;
254 |
255 | elsif ((bit_count >= "00111000") and (bit_count <= "01001011")) then -- bit count 56 to 75
256 |
257 | -- Slot 3 : left channel
258 |
259 | ac97_sdata_out <= latch_left_data(19); -- send out bits and rotate through 20 bit word
260 |
261 | latch_left_data <= latch_left_data(18 downto 0) & latch_left_data(19);
262 |
263 | elsif ((bit_count >= "01001100") and (bit_count <= "01011111")) then -- bit count 76 to 95
264 | -- Slot 4 : right channel
265 |
266 | ac97_sdata_out <= latch_right_data(95 - to_integer(unsigned(bit_count)));
267 |
268 | else
269 | ac97_sdata_out <= '0';
270 | end if;
271 |
272 | -- incriment bit counter
273 | bit_count <= std_logic_vector(unsigned(bit_count) + 1);
274 | end if;
275 | end process;
276 |
277 | -- ac97 link input frame [frame coming in]
278 | ---------------------------------------------------------------------------
279 | process (ac97_bitclk)
280 | begin
281 | if (ac97_bitclk'event and ac97_bitclk = '0') then -- clock on falling edge of bitclk
282 | if (bit_count >= "00111001") and (bit_count <= "01001100") then -- from 115 to 76
283 | -- Slot 3 : left channel
284 | left_in_data <= left_in_data(18 downto 0) & ac97_sdata_in; -- concat incoming bits on end
285 | elsif (bit_count >= "01001101") and (bit_count <= "01100000") then -- from 77 to 96
286 | -- Slot 4 : right channel
287 | right_in_data <= right_in_data(18 downto 0) & ac97_sdata_in; -- concat incoming bits on end
288 | end if;
289 | end if;
290 | end process;
291 |
292 | end arch;
293 |
294 |
295 | --/////////////// STATE MACHINE TO CONFIGURE THE AC97 ///////////////////////////--
296 |
297 |
298 |
299 | library ieee;
300 | use ieee.std_logic_1164.all;
301 | use ieee.numeric_std.all;
302 |
303 | -- one can add extra inputs and signals to control the input to LM4550 (ac97) register map below see volume and source for example
304 | entity ac97cmd is
305 | port (
306 | clk : in std_logic;
307 | ac97_ready_sig : in std_logic;
308 | cmd_addr : out std_logic_vector(7 downto 0);
309 | cmd_data : out std_logic_vector(15 downto 0);
310 | latching_cmd : out std_logic;
311 | volume : in std_logic_vector(4 downto 0); -- input for encoder for volume control 0->31
312 | source : in std_logic_vector(2 downto 0)); -- 000 = Mic, 100=LineIn
313 | end ac97cmd;
314 |
315 |
316 |
317 | architecture arch of ac97cmd is
318 | signal cmd : std_logic_vector(23 downto 0);
319 | signal atten : std_logic_vector(4 downto 0); -- used to set atn in 04h ML4:0/MR4:0
320 | type state_type is (S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11);
321 | signal cur_state, next_state : state_type;
322 | begin
323 | -- parse command from data
324 | cmd_addr <= cmd(23 downto 16);
325 | cmd_data <= cmd(15 downto 0);
326 | atten <= std_logic_vector(31 - unsigned(volume)); -- convert vol to attenuation
327 |
328 | -- USED TO DETERMINE IF THE REGISTER ADDRESS IS VALID
329 | -- one can add more with select statments with output signals to do more error checking
330 | ---------------------------------------------------------------------------------------------
331 | with cmd(23 downto 16) select
332 | latching_cmd <=
333 | '1' when X"02" | X"04" | X"06" | X"0A" | X"0C" | X"0E" | X"10" | X"12" | X"14" |
334 | X"16" | X"18" | X"1A" | X"1C" | X"20" | X"22" | X"24" | X"26" | X"28" |
335 | X"2A" | X"2C" | X"32" | X"5A" | X"74" | X"7A" | X"7C" | X"7E" | X"80",
336 | '0' when others;
337 |
338 |
339 | -- go through states based on input pulses from ac97 ready signal
340 | ------------------------------------------------------------------------------------------
341 | process(clk, next_state, cur_state)
342 | begin
343 |
344 | if(clk'event and clk = '1') then
345 | if ac97_ready_sig = '1' then
346 | cur_state <= next_state;
347 | end if;
348 | end if;
349 | end process;
350 |
351 |
352 | -- use state machine to configure controller
353 | -- refer to register map on LM4550 data sheet
354 | -- signals and input busses can be added to control
355 | -- the AC97 codec refer to the source and volume to see how
356 | -- first part is address, second part after _ is command
357 | -- states and input signals can be added for real time configuration of
358 | -- any ac97 register
359 | -------------------------------------------------------------------------------------------
360 | process (next_state, cur_state, atten, source)
361 | begin
362 |
363 | case cur_state is
364 | when S0 =>
365 | cmd <= X"02_8000"; -- master volume 0 0000->0dB atten, 1 1111->46.5dB atten
366 | next_state <= S2;
367 | when S1 =>
368 | cmd <= X"04" & "000" & atten & "000" & atten; -- headphone volume
369 | next_state <= S4;
370 | when S2 =>
371 | cmd <= X"0A_0000"; -- Set pc_beep volume
372 | next_state <= S11;
373 | when S3 =>
374 | cmd <= X"0E_8048"; -- Mic Volume set to gain of +20db
375 | next_state <= S10;
376 | when S4 =>
377 | cmd <= X"18_0808"; -- PCM volume
378 | next_state <= S6;
379 | when S5 =>
380 | cmd <= X"1A" & "00000" & source & "00000" & source; -- Record select reg 000->Mic, 001->CD in l/r, 010->Video in l/r, 011->aux in l/r
381 | next_state <= S7; -- 100->line_in l/r, 101->stereo mix, 110->mono mix, 111->phone input
382 | when S6 =>
383 | cmd <= X"1C_0F0F"; -- Record gain set to max (22.5dB gain)
384 | next_state <= S8;
385 | when S7 =>
386 | cmd <= X"20_8000"; -- PCM out path 3D audio bypassed
387 | next_state <= S0;
388 | when S8 =>
389 | cmd <= X"2C_BB80"; -- DAC rate 48 KHz, can be set to 1F40 = 8Khz, 2B11 = 11.025KHz, 3E80 = 16KHz,
390 | next_state <= S5; -- 5622 = 22.05KHz, AC44 = 44.1KHz, BB80 = 48KHz
391 | when S9 =>
392 | cmd <= X"32_BB80"; -- ADC rate 48 KHz, can be set to 1F40 = 8Khz, 2B11 = 11.025KHz, 3E80 = 16KHz,
393 | next_state <= S3; -- 5622 = 22.05KHz, AC44 = 44.1KHz, BB80 = 48KHz
394 | when S10 =>
395 | cmd <= X"80_0000";
396 | next_state <= S9;
397 | when S11 =>
398 | cmd <= X"80_0000";
399 | next_state <= S1;
400 | end case;
401 |
402 | end process;
403 |
404 | end arch;
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
--------------------------------------------------------------------------------
/ac97/ac97_top.vhd:
--------------------------------------------------------------------------------
1 | -- Wrapper module for AC97 output from PCM lines
2 | --
3 |
4 | library IEEE;
5 | use IEEE.STD_LOGIC_1164.ALL;
6 |
7 | -- Uncomment the following library declaration if using
8 | -- arithmetic functions with Signed or Unsigned values
9 | use IEEE.NUMERIC_STD.ALL;
10 |
11 | use work.Spar6_Parts.all;
12 |
13 | entity ac97_output is
14 | port (
15 | CLK : in std_logic; -- 100 MHz Clock
16 | RSTN : in std_logic;
17 |
18 | -- AC97 Ports
19 |
20 | BITCLK : in STD_LOGIC;
21 | AUDSDI : in STD_LOGIC;
22 | AUDSDO : out STD_LOGIC;
23 | AUDSYNC : out STD_LOGIC;
24 | AUDRST : out STD_LOGIC;
25 |
26 | -- Asynchronous PCM Input
27 | PCM_in_left : in std_logic_vector(15 downto 0);
28 | PCM_in_right: in std_logic_vector(15 downto 0)
29 | );
30 | end ac97_output;
31 |
32 | -- ///////////////////////////////////////////////
33 | -- // ATTENTION!!! //
34 | -- // - - - - - - - - - - - - - - - - - - - - - //
35 | -- // //
36 | -- // The following code is copy-pasted crap //
37 | -- // //
38 | -- // (In your own interest) //
39 | -- // DO NOT READ!!! //
40 | -- ///////////////////////////////////////////////
41 |
42 | architecture Behavioral of ac97_output is
43 |
44 | signal L_bus, R_bus, L_bus_out, R_bus_out : std_logic_vector(17 downto 0);
45 | signal cmd_addr : std_logic_vector(7 downto 0);
46 | signal cmd_data : std_logic_vector(15 downto 0);
47 | signal ready : std_logic;
48 | signal latching_cmd : std_logic;
49 |
50 | constant VOLUME : std_logic_vector(4 downto 0) := "01011";
51 | constant SOURCE : std_logic_vector(2 downto 0) := "000";
52 |
53 | begin
54 | -- INSTANTIATE BOTH THE MAIN DRIVER AND AC97 CHIP CONFIGURATION STATE-MACHINE
55 | -------------------------------------------------------------------------------
56 | ac97_cont0 : entity work.ac97(arch)
57 | port map(n_reset => rstn, clk => clk, ac97_sdata_out => AUDSDO, ac97_sdata_in => AUDSDI, latching_cmd => latching_cmd ,
58 | ac97_sync => AUDSYNC, ac97_bitclk => BITCLK, ac97_n_reset => AUDRST, ac97_ready_sig => ready,
59 | L_out => L_bus, R_out => R_bus, L_in => L_bus_out, R_in => R_bus_out, cmd_addr => cmd_addr, cmd_data => cmd_data);
60 |
61 | ac97cmd_cont0 : entity work.ac97cmd(arch)
62 | port map (clk => clk, ac97_ready_sig => ready, cmd_addr => cmd_addr, cmd_data => cmd_data, volume => VOLUME,
63 | source => SOURCE, latching_cmd => latching_cmd);
64 |
65 | -- Latch output back into input for Talkthrough testing
66 | -- this process can be replaced with a user component for signal processing
67 | -------------------------------------------------------------------------------
68 |
69 | process ( clk, rstn, L_bus_out, R_bus_out, PCM_in_left, PCM_in_right)
70 | begin
71 | if (clk'event and clk = '1') then
72 | if rstn = '0' then
73 | L_bus <= (others => '0');
74 | R_bus <= (others => '0');
75 | elsif(ready = '1') then
76 | L_bus <= std_logic_vector(PCM_in_left) & "00";
77 | R_bus <= std_logic_vector(PCM_in_right) & "00";
78 | end if;
79 | end if;
80 | end process;
81 |
82 | end Behavioral;
83 |
84 |
85 |
--------------------------------------------------------------------------------
/doc/implementation-issues.txt:
--------------------------------------------------------------------------------
1 | - Tristate vs. Multiplexer design
2 | - Tristate are not implemented in FPGA hardware nowadays, but for compatibility reasons, automatic
3 | transformation to mux busses are well-supported in synthesizer, allthough not recommended
4 | - Dan Leach's 6502 core was a tristate design
5 | - Bad coding leads to undefined simulation states and possible mismatches
6 | - Multiplexer design in new code
7 | - Some NES Components have an external discrete address decoder. This makes sense to combine with a muxer design, because the decoding logic lands in one hierarchical block
8 | - Input registers and APU are placed and decoded inside the CPU chip, but can equally be distinct devices on the memory bus. Only drawback is that the APU needs a connection to the internal CPU clock
9 | - Muxes imply a different design, because address decoding is moved outside of the device.
10 |
--------------------------------------------------------------------------------
/doc/myhdl-findings.txt:
--------------------------------------------------------------------------------
1 | Conversion does not yet support class attribute signals
2 |
3 | http://referaat.cs.utwente.nl/conference/17/paper/7344/comparing-hardware-description-languages.pdf
4 |
5 | google scholar more on myhdl
6 |
--------------------------------------------------------------------------------
/doc/teaching.doc:
--------------------------------------------------------------------------------
1 | possible goals to achieve in a lab:
2 |
3 | write a controller automation in myhdl for debugging
4 |
5 | implement an apu feature like sweep
6 |
7 | modify an existing mapper infrastructure to run a new game
8 |
9 | implement a video scaler
10 |
11 | implement a missing apu feature like sweep
12 |
--------------------------------------------------------------------------------
/doc/todo.txt:
--------------------------------------------------------------------------------
1 | Fix various synthesis warnings
2 | SpriteOverflow Flag is apparently unused
3 |
4 | APU Details
5 | Pulse Sweep (Candidate for student implementation)
6 | Status Registers
7 | Interrupts
8 | DMC
9 |
10 | Graphic Glitches:
11 | Sprite Y Flip
12 | Wrong attributes in second column
13 | Border Glitch / Nicer blending
14 |
15 |
16 |
17 | Interesting Demonstration tool: put some graphic/audio feature enables to switches
18 | Halt and single frame stepping
19 | Advanced: Manual Scrolling with joypad
20 |
21 | Infrastructure for mapper support
22 | Idea: Use mappers for design partitioning and partial reconfiguration
23 |
24 |
25 | Documentation
26 | Write build instructions
27 | Simplify things like py65 PYTHONPATH
28 | Write some comments
29 |
30 | Overview of each module / c&p into thesis
31 |
--------------------------------------------------------------------------------
/doc/tutorial-genesys.txt:
--------------------------------------------------------------------------------
1 | WRITE IT!!!
2 |
3 | target flash settings:
4 | - Parallel BPI Single Boot
5 | - Virtex 5 / 32M
6 | - 16 bit width
7 | - 28F256P30 (not 512, schematic is lying)
8 |
--------------------------------------------------------------------------------
/nes_top.ucf:
--------------------------------------------------------------------------------
1 | # This file is a general .ucf for Genesys rev C board
2 | # To use it in a project:
3 | # - remove or comment the lines corresponding to unused pins
4 | # - rename the used signals according to the project
5 | # clock pin for Genesys rev C board
6 | #TIMESPEC TS_clk = PERIOD clk 100000 kHz;
7 | NET "clk" LOC = "AG18"; # Bank = 4, Pin name = IO_L6P_GC_4, Type = GCLK, Sch name = GCLK0
8 | NET "rstn" LOC = "E7" |IOSTANDARD = LVCMOS25 |PULLUP;
9 |
10 | # onboard HDMI
11 | NET "HDMIHSYNC" LOC = "H8"; # Bank = 20, Pin name = IO_L3N_20, Sch name = HDMI-H
12 | NET "HDMIVSYNC" LOC = "F13"; # Bank = 20, Pin name = IO_L19P_20, Sch name = HDMI-V
13 | NET "HDMIDE" LOC = "V10"; # Bank = 18, Pin name = IO_L19P_18, Sch name = HDMI-DE
14 | NET "HDMICLKP" LOC = "K11"; # Bank = 20, Pin name = IO_L5P_20, Sch name = HDMI-CLK_P
15 | NET "HDMICLKN" LOC = "J11"; # Bank = 20, Pin name = IO_L5N_20, Sch name = HDMI-CLK_N
16 | NET "HDMID<0>" LOC = "G10"; # Bank = 20, Pin name = IO_L2N_20, Sch name = HDMI-D0
17 | NET "HDMID<1>" LOC = "G8"; # Bank = 20, Pin name = IO_L3P_20, Sch name = HDMI-D1
18 | NET "HDMID<2>" LOC = "B12"; # Bank = 20, Pin name = IO_L8N_CC_20, Sch name = HDMI-D2
19 | NET "HDMID<3>" LOC = "D12"; # Bank = 20, Pin name = IO_L6P_20, Sch name = HDMI-D3
20 | NET "HDMID<4>" LOC = "C12"; # Bank = 20, Pin name = IO_L6N_20, Sch name = HDMI-D4
21 | NET "HDMID<5>" LOC = "D11"; # Bank = 20, Pin name = IO_L4P_20, Sch name = HDMI-D5
22 | NET "HDMID<6>" LOC = "F10"; # Bank = 20, Pin name = IO_L2P_20, Sch name = HDMI-D6
23 | NET "HDMID<7>" LOC = "D10"; # Bank = 20, Pin name = IO_L4N_VREF_20, Sch name = HDMI-D7
24 | NET "HDMID<8>" LOC = "E9"; # Bank = 20, Pin name = IO_L0P_20, Sch name = HDMI-D8
25 | NET "HDMID<9>" LOC = "F9"; # Bank = 20, Pin name = IO_L1P_20, Sch name = HDMI-D9
26 | NET "HDMID<10>" LOC = "E8"; # Bank = 20, Pin name = IO_L0N_20, Sch name = HDMI-D10
27 | NET "HDMID<11>" LOC = "F8"; # Bank = 20, Pin name = IO_L1N_20, Sch name = HDMI-D11
28 | NET "HDMISCL" LOC = "U8"; # Bank = 18, Pin name = IO_L17N_18, Sch name = I2C-SCL
29 | NET "HDMISDA" LOC = "V8"; # Bank = 18, Pin name = IO_L17P_18, Sch name = I2C-SDA
30 | #NET "HDMIHPINT" LOC = "W9"; # Bank = 18, Pin name = IO_L15N_18, Sch name = HDMI-HPINT
31 | NET "HDMIRSTN" LOC = "AF23"; # Bank = 2, Pin name = IO_L1P_CC_A25_2, Sch name = HDMI-RST
32 |
33 |
34 | NET "AUDCLK" LOC = "AH17"; # Bank = 4, Pin name = IO_L7P_GC_VRN_4, Sch name = AUD-BIT-CLK
35 | NET "AUDSDI" LOC = "AE18"; # Bank = 4, Pin name = IO_L8N_CC_GC_4, Sch name = AUD-SDI
36 | NET "AUDSDO" LOC = "AG20"; # Bank = 4, Pin name = IO_L4N_GC_VREF_4, Sch name = AUD-SDO
37 | NET "AUDSYNC" LOC = "J9"; # Bank = 20, Pin name = IO_L9N_CC_20, Sch name = AUD-SYNC
38 | NET "AUDRST" LOC = "E12"; # Bank = 20, Pin name = IO_L17P_20, Sch name = AUD-RESET
39 |
40 |
41 | NET "Led<0>" LOC = "AG8"; # Bank = 22, Pin name = IO_L18P_22, Sch name = LD0
42 | NET "Led<1>" LOC = "AH8"; # Bank = 22, Pin name = IO_L18N_22, Sch name = LD1
43 | NET "Led<2>" LOC = "AH9"; # Bank = 22, Pin name = IO_L17P_22, Sch name = LD2
44 | NET "Led<3>" LOC = "AG10"; # Bank = 22, Pin name = IO_L19P_22, Sch name = LD3
45 | NET "Led<4>" LOC = "AH10"; # Bank = 22, Pin name = IO_L17N_22, Sch name = LD4
46 | NET "Led<5>" LOC = "AG11"; # Bank = 22, Pin name = IO_L19N_22, Sch name = LD5
47 | NET "Led<6>" LOC = "AF11"; # Bank = 22, Pin name = IO_L16P_22, Sch name = LD6
48 | NET "Led<7>" LOC = "AE11"; # Bank = 22, Pin name = IO_L16N_22, Sch name = LD7
49 |
50 | NET "BTN<0>" LOC = "G6"; # Bank = 12, Pin name = IO_L17P_12, Sch name = BTN0
51 | NET "BTN<1>" LOC = "G7"; # Bank = 12, Pin name = IO_L17N_12, Sch name = BTN1
52 | NET "BTN<2>" LOC = "E6"; # Bank = 12, Pin name = IO_L19P_12, Sch name = BTNU
53 | NET "BTN<3>" LOC = "J17"; # Bank = 3, Pin name = IO_L4N_GC_VREF_3, Sch name = BTNR
54 | NET "BTN<4>" LOC = "H15"; # Bank = 3, Pin name = IO_L6N_GC_3, Sch name = BTND
55 | NET "BTN<5>" LOC = "K19"; # Bank = 3, Pin name = IO_L5N_GC_3, Sch name = BTNL
56 | NET "BTN<6>" LOC = "J21"; # Bank = 3, Pin name = IO_L7N_GC_3, Sch name = BTNS
57 |
58 | # onBoard SWITCHES
59 | NET "sw<0>" LOC = "J19"; # Bank = 3, Pin name = IO_L3N_GC_3, Sch name = SW0
60 | NET "sw<1>" LOC = "L18"; # Bank = 3, Pin name = IO_L1N_CC_GC_3, Sch name = SW1
61 | NET "sw<2>" LOC = "K18"; # Bank = 3, Pin name = IO_L3P_GC_3, Sch name = SW2
62 | NET "sw<3>" LOC = "H18"; # Bank = 3, Pin name = IO_L0N_CC_GC_3, Sch name = SW3
63 | NET "sw<4>" LOC = "H17"; # Bank = 3, Pin name = IO_L0P_CC_GC_3, Sch name = SW4
64 | NET "sw<5>" LOC = "K17"; # Bank = 3, Pin name = IO_L1P_CC_GC_3, Sch name = SW5
65 | NET "sw<6>" LOC = "G16"; # Bank = 3, Pin name = IO_L2N_GC_VRP_3, Sch name = SW6
66 | NET "sw<7>" LOC = "G15"; # Bank = 3, Pin name = IO_L2P_GC_VRN_3, Sch name = SW7
67 |
68 |
69 | # PMOD Connectors
70 | NET "JA<0>" LOC = "AD11"; # BANK = 22, Pin name = IO_L10N_CC_22, Sch name = JA1
71 | NET "JA<1>" LOC = "AD9"; # BANK = 22, Pin name = IO_L9N_CC_22, Sch name = JA2
72 | NET "JA<2>" LOC = "AM13"; # BANK = 22, Pin name = IO_L2N_22, Sch name = JA3
73 | NET "JA<3>" LOC = "AM12"; # BANK = 22, Pin name = IO_L6P_22, Sch name = JA4
74 | NET "JA<4>" LOC = "AD10"; # BANK = 22, Pin name = IO_L10P_CC_22, Sch name = JA7
75 | NET "JA<5>" LOC = "AE8"; # BANK = 22, Pin name = IO_L9P_CC_22, Sch name = JA8
76 | NET "JA<6>" LOC = "AF10"; # BANK = 22, Pin name = IO_L14N_VREF_22, Sch name = JA9
77 | NET "JA<7>" LOC = "AJ11"; # BANK = 22, Pin name = IO_L11N_CC_22, Sch name = JA10
78 |
79 | NET "JB<0>" LOC = "AE9"; # BANK = 22, Pin name = IO_L12N_VRP_22, Sch name = JB1
80 | NET "JB<1>" LOC = "AC8"; # BANK = 22, Pin name = IO_L5P_22, Sch name = JB2
81 | NET "JB<2>" LOC = "AB10"; # BANK = 22, Pin name = IO_L1P_22, Sch name = JB3
82 | NET "JB<3>" LOC = "AC9"; # BANK = 22, Pin name = IO_L7N_22, Sch name = JB4
83 | NET "JB<4>" LOC = "AF8"; # BANK = 22, Pin name = IO_L12P_VRN_22, Sch name = JB7
84 | NET "JB<5>" LOC = "AB8"; # BANK = 22, Pin name = IO_L5N_22, Sch name = JB8
85 | NET "JB<6>" LOC = "AA10"; # BANK = 22, Pin name = IO_L1N_22, Sch name = JB9
86 | NET "JB<7>" LOC = "AA9"; # BANK = 22, Pin name = IO_L3N_22, Sch name = JB10
87 |
88 | # NET "JC<0>" LOC = "AL11"; # BANK = 22, Pin name = IO_L8P_CC_22, Sch name = JC1
89 | # NET "JC<1>" LOC = "AJ10"; # BANK = 22, Pin name = IO_L15N_22, Sch name = JC2
90 | # NET "JC<2>" LOC = "AK9"; # BANK = 22, Pin name = IO_L13N_22, Sch name = JC3
91 | # NET "JC<3>" LOC = "AF9"; # BANK = 22, Pin name = IO_L14P_22, Sch name = JC4
92 | # NET "JC<4>" LOC = "AK11"; # BANK = 22, Pin name = IO_L11P_CC_22, Sch name = JC7
93 | # NET "JC<5>" LOC = "AC10"; # BANK = 22, Pin name = IO_L7P_22, Sch name = JC8
94 | # NET "JC<6>" LOC = "AJ9"; # BANK = 22, Pin name = IO_L15P_22, Sch name = JC9
95 | # NET "JC<7>" LOC = "AA8"; # BANK = 22, Pin name = IO_L3P_22, Sch name = JC10
96 | #
97 | # NET "JD<0>" LOC = "AN14"; # BANK = 22, Pin name = IO_L0P_22, Sch name = JD1
98 | # NET "JD<1>" LOC = "AN13"; # BANK = 22, Pin name = IO_L2P_22, Sch name = JD2
99 | # NET "JD<2>" LOC = "AP12"; # BANK = 22, Pin name = IO_L4P_22, Sch name = JD3
100 | # NET "JD<3>" LOC = "AL10"; # BANK = 22, Pin name = IO_L8N_CC_22, Sch name = JD4
101 | # NET "JD<4>" LOC = "AP14"; # BANK = 22, Pin name = IO_L0N_22, Sch name = JD7
102 | # NET "JD<5>" LOC = "AN12"; # BANK = 22, Pin name = IO_L4N_VREF_22, Sch name = JD8
103 | # NET "JD<6>" LOC = "AM11"; # BANK = 22, Pin name = IO_L6N_22, Sch name = JD9
104 | # NET "JD<7>" LOC = "AK8"; # BANK = 22, Pin name = IO_L13P_22, Sch name = JD10
105 |
106 | #Created by Constraints Editor (xc5vlx50t-ff1136-2) - 2011/11/11
107 | NET "CLK" TNM_NET = CLK;
108 | TIMESPEC TS_CLK = PERIOD "CLK" 10 ns HIGH 50%;
109 |
110 |
--------------------------------------------------------------------------------
/roms/fpgasmb.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strfry/nesfpga/5059f4b2394c47a59518675b1a63adfc50ff1b63/roms/fpgasmb.nes
--------------------------------------------------------------------------------
/roms/smb.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strfry/nesfpga/5059f4b2394c47a59518675b1a63adfc50ff1b63/roms/smb.nes
--------------------------------------------------------------------------------
/roms/stress.nes:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strfry/nesfpga/5059f4b2394c47a59518675b1a63adfc50ff1b63/roms/stress.nes
--------------------------------------------------------------------------------
/synaesthesia/genesys.xst:
--------------------------------------------------------------------------------
1 | -p xc5vlx50t-ff1136-2
2 |
3 | # Disable iobufs for submodules
4 |
5 | -iobuf NO -bufg 0 -iob false
6 |
7 | # Optimization Level
8 |
9 | -opt_level 0
10 |
11 |
12 | # Other Options
13 |
14 | -power NO
15 | -iuc YES
16 | -keep_hierarchy No
17 | -netlist_hierarchy As_Optimized
18 | -rtlview Yes
19 | -glob_opt AllClockNets
20 | -read_cores NO
21 | -write_timing_constraints NO
22 | -cross_clock_analysis NO
23 | -hierarchy_separator /
24 | -bus_delimiter <>
25 | -case Maintain
26 | -slice_utilization_ratio 100
27 | -bram_utilization_ratio 100
28 | -dsp_utilization_ratio 100
29 | -lc Off
30 | -reduce_control_sets Off
31 | -verilog2001 YES
32 | -fsm_extract YES -fsm_encoding Auto
33 | -safe_implementation No
34 | -fsm_style LUT
35 | -ram_extract Yes
36 | -ram_style Auto
37 | -rom_extract Yes
38 | -mux_style Auto
39 | -decoder_extract YES
40 | -priority_extract Yes
41 | -shreg_extract YES
42 | -shift_extract YES
43 | -xor_collapse YES
44 | -rom_style Auto
45 | -auto_bram_packing NO
46 | -mux_extract Yes
47 | -resource_sharing YES
48 | -async_to_sync NO
49 | -use_dsp48 Auto
50 | -max_fanout 100000
51 | -register_duplication YES
52 | -register_balancing No
53 | -slice_packing YES
54 | -optimize_primitives NO
55 | -use_clock_enable Auto
56 | -use_sync_set Auto
57 | -use_sync_reset Auto
58 | -equivalent_register_removal YES
59 | -slice_utilization_ratio_maxmargin 5
60 |
61 | #-bufg 32
62 | #-iobuf YES
63 | #-iob Auto
64 |
--------------------------------------------------------------------------------
/synaesthesia/run_xst.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | #
3 | # Workaround to launch xst manually
4 | #
5 |
6 | XILINX_HOME=/opt/cad/xilinx/xilinx-13.1-64b/ISE_DS/ISE
7 |
8 | # echo run -ifn watchvhd.vhd -ifmt VHDL -ofn watchvhd.ngc -ofmt NGC -p xcv50-bg256-6 -opt_mode Speed
9 |
10 | # Workaround for random runtime linker errors.
11 | # Debugging stuff like this is not exactly fun.
12 | # Seriously Xilinx, fuck you guys!.
13 |
14 | FUBAR=$FUBAR:$XILINX_HOME/lib/lin64/libboost_serialization-gcc41-mt-p-1_38.so.1.38.0
15 | FUBAR=$FUBAR:$XILINX_HOME/lib/lin64/libAntlr.so
16 | FUBAR=$FUBAR:$XILINX_HOME/lib/lin64/libXst2_CoreData.so
17 |
18 | # Oh, and XST reads on stdin for more what should be command line parameters
19 | CONFIG=$(grep -E ^[^\#] $XST_CONFIG)
20 |
21 | #XST_PARAMS="$@ "$(grep -E ^[^\#] $XST_CONFIG)
22 |
23 | # And set a temp dir to box up all the crap XST generates
24 |
25 | DIRSET_CMD='set -tmpdir "xst/projnav.tmp"\nset -xsthdpdir "xst"\n'
26 | CONFIG=$(grep -E ^[^\#] $XST_CONFIG)
27 |
28 |
29 | export LD_PRELOAD=$FUBAR
30 | echo -e $DIRSET_CMD run $CONFIG $@ | xst
31 |
--------------------------------------------------------------------------------
/synaesthesia/synthesis.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | # Set up Xilinx environment variable
4 |
5 | XILINX_SETTINGS_PATH = "/opt/Xilinx/14.5/ISE_DS/settings64.sh"
6 |
7 | def shell_source(script):
8 | """Sometime you want to emulate the action of "source" in bash,
9 | settings some environment variables. Here is a way to do it."""
10 | import subprocess, os
11 | pipe = subprocess.Popen(". %s; env" % script, stdout=subprocess.PIPE, shell=True)
12 | output = pipe.communicate()[0]
13 | print output
14 | env = zip((line.split("=", 1) for line in output.splitlines()))
15 | os.environ.update(env)
16 |
17 | #shell_source(XILINX_SETTINGS_PATH)
18 |
19 | from xilinx import Fpga, Xilinx
20 |
21 | # Specify UCF-like pin mappings
22 |
23 | fpga = Fpga()
24 | fpga.ucf_file = "nes_top.ucf"
25 | fpga.setDevice('virtex5', 'xc5vlx50t', 'ff1136', '-2')
26 |
27 | fpga.setPin("clk", "AG18")
28 | fpga.setPin("rstn", "E7")
29 | fpga.setPin("HDMIHSYNC", "H8")
30 | fpga.setPin("HDMIVSYNC", "F13")
31 | fpga.setPin("HDMIDE", "V10")
32 | fpga.setPin("HDMICLKP", "K11")
33 | fpga.setPin("HDMICLKN", "J11")
34 | fpga.setPin("HDMID<0>", "G10")
35 | fpga.setPin("HDMID<1>", "G8")
36 | fpga.setPin("HDMID<2>", "B12")
37 | fpga.setPin("HDMID<3>", "D12")
38 | fpga.setPin("HDMID<4>", "C12")
39 | fpga.setPin("HDMID<5>", "D11")
40 | fpga.setPin("HDMID<6>", "F10")
41 | fpga.setPin("HDMID<7>", "D10")
42 | fpga.setPin("HDMID<8>", "E9")
43 | fpga.setPin("HDMID<9>", "F9")
44 | fpga.setPin("HDMID<10>", "E8")
45 | fpga.setPin("HDMID<11>", "F8")
46 | fpga.setPin("HDMISCL", "U8")
47 | fpga.setPin("HDMISDA", "V8")
48 | fpga.setPin("HDMIRSTN", "AF23")
49 | fpga.setPin("AUDCLK", "AH17")
50 | fpga.setPin("AUDSDI", "AE18")
51 | fpga.setPin("AUDSDO", "AG20")
52 | fpga.setPin("AUDSYNC", "J9")
53 | fpga.setPin("AUDRST", "E12")
54 | fpga.setPin("Led<0>", "AG8")
55 | fpga.setPin("Led<1>", "AH8")
56 | fpga.setPin("Led<2>", "AH9")
57 | fpga.setPin("Led<3>", "AG10")
58 | fpga.setPin("Led<4>", "AH10")
59 | fpga.setPin("Led<5>", "AG11")
60 | fpga.setPin("Led<6>", "AF11")
61 | fpga.setPin("Led<7>", "AE11")
62 | fpga.setPin("BTN<0>", "G6")
63 | fpga.setPin("BTN<1>", "G7")
64 | fpga.setPin("BTN<2>", "E6")
65 | fpga.setPin("BTN<3>", "J17")
66 | fpga.setPin("BTN<4>", "H15")
67 | fpga.setPin("BTN<5>", "K19")
68 | fpga.setPin("BTN<6>", "J21")
69 | fpga.setPin("sw<0>", "J19")
70 | fpga.setPin("sw<1>", "L18")
71 | fpga.setPin("sw<2>", "K18")
72 | fpga.setPin("sw<3>", "H18")
73 | fpga.setPin("sw<4>", "H17")
74 | fpga.setPin("sw<5>", "K17")
75 | fpga.setPin("sw<6>", "G16")
76 | fpga.setPin("sw<7>", "G15")
77 | fpga.setPin("JA<0>", "AD11")
78 | fpga.setPin("JA<1>", "AD9")
79 | fpga.setPin("JA<2>", "AM13")
80 | fpga.setPin("JA<3>", "AM12")
81 | fpga.setPin("JA<4>", "AD10")
82 | fpga.setPin("JA<5>", "AE8")
83 | fpga.setPin("JA<6>", "AF10")
84 | fpga.setPin("JA<7>", "AJ11")
85 | fpga.setPin("JB<0>", "AE9")
86 | fpga.setPin("JB<1>", "AC8")
87 | fpga.setPin("JB<2>", "AB10")
88 | fpga.setPin("JB<3>", "AC9")
89 | fpga.setPin("JB<4>", "AF8")
90 | fpga.setPin("JB<5>", "AB8")
91 | fpga.setPin("JB<6>", "AA10")
92 | fpga.setPin("JB<7>", "AA9")
93 |
94 | print fpga
95 |
96 | # Specify build files
97 |
98 | _ = '../'
99 |
100 | imp = Xilinx('build', 'Genesys_NES')
101 | imp.setFpga(fpga)
102 |
103 | # Add HDL Files
104 |
105 | # Top File for Digilent Genesys Board
106 | imp.addHdl(_ + './Genesys_NES.vhd')
107 |
108 | # NES Core
109 | imp.addHdl(_ + './NES_Pack.vhd')
110 | imp.addHdl(_ + './NES_Mainboard.vhd')
111 |
112 | # Cartridge Module for coregen files
113 | imp.addHdl(_ + 'CartridgeROM_Coregen.vhd')
114 |
115 | # Coregen files for Super Mario
116 | imp.addHdl(_ + 'Coregen/chr_rom_smb.ngc')
117 | imp.addHdl(_ + 'Coregen/prg_rom_smb.ngc')
118 |
119 | # NES CPU
120 | imp.addHdl(_ + './NES_2A03/T65.vhd')
121 | imp.addHdl(_ + './NES_2A03/SRAM.vhd')
122 | imp.addHdl(_ + './NES_2A03/T65_ALU.vhd')
123 | imp.addHdl(_ + './NES_2A03/Dan_2A03.vhd')
124 | imp.addHdl(_ + './NES_2A03/T65_MCode.vhd')
125 | imp.addHdl(_ + './NES_2A03/T65_Pack.vhd')
126 | imp.addHdl(_ + './NES_2A03/ClockDivider.vhd')
127 | imp.addHdl(_ + './NES_2A03/DanPack.vhd')
128 |
129 | # NES APU
130 |
131 | imp.addHdl(_ + 'myhdl/APU_Main.v')
132 |
133 | # NES PPU
134 |
135 | imp.addHdl(_ + './PPU/PPU.vhd')
136 | imp.addHdl(_ + './PPU/TileFetcher.vhd')
137 | imp.addHdl(_ + './PPU/PPU_Pack.vhd')
138 | imp.addHdl(_ + './PPU/Loopy_Scrolling.vhd')
139 | imp.addHdl(_ + './PPU/SpriteSelector.vhd')
140 |
141 | # HDMI Output
142 | imp.addHdl(_ + './HDMI/HDMIController.vhd')
143 | imp.addHdl(_ + './HDMI/tft_interface.v')
144 | imp.addHdl(_ + 'HDMI/HDMIController.vhd')
145 | imp.addHdl(_ + 'HDMI/iic_init.v')
146 | imp.addHdl(_ + './ColorPalette.vhd')
147 |
148 | # AC97 Audio Output
149 |
150 | imp.addHdl(_ + './ac97/ac97_top.vhd')
151 | imp.addHdl(_ + './ac97/Talkthrough_Parts.vhd')
152 |
153 | imp.createTcl(target = 'Generate Programming File')
154 |
155 | # Run
156 | imp.run()
157 |
--------------------------------------------------------------------------------
/synaesthesia/vhd2ngc:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | ./run_xst.sh run -ifn $1 -ifmt VHDL -ofn $2 -ofmt NGC
3 |
--------------------------------------------------------------------------------
/synaesthesia/xilinx.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import popen2
4 | import sys, os
5 | from time import gmtime, strftime
6 |
7 |
8 | #######################################################################
9 | #
10 | #
11 | class Xilinx(object):
12 | '''Python classe to run implementation tools
13 |
14 | TODO: make it modular to allow other implementation tools
15 | '''
16 | def __init__(self, path=None, top_name=None):
17 | self.path = '.'
18 | if path is not None:
19 | # TODO: add some code to have path separator all the same
20 | # either windows or linux, depending on what system it runs
21 | self.path = path
22 | self.fpga = None
23 | self.tcl_name = None
24 | self.top_name = None
25 | if top_name:
26 | if isinstance(top_name, str):
27 | self.top_name = top_name
28 | self.tcl_name = top_name + '.tcl'
29 | else:
30 | raise TypeError('top_name needs to be string')
31 | self.hdl_fileL = []
32 |
33 | # start with the text string for the TCL script
34 | self.tcl_script = '#\n#\n# ISE implementation script\n'
35 | date_time = strftime("%a, %d %b %Y %H:%M:%S +0000", gmtime())
36 | self.tcl_script += '# create: %s\n'%date_time
37 | self.tcl_script += '# by: %s\n'%os.path.basename(sys.argv[0])
38 | self.tcl_script += '#\n#\n'
39 |
40 | def addHdl(self, files):
41 | '''Add HDL files to the project
42 | files : string or list of strings
43 | '''
44 | if isinstance(files, str):
45 | self.hdl_fileL.append(files)
46 |
47 | elif isinstance(files, (list, tuple)):
48 |
49 | for f in files:
50 | if not isinstance(f, str):
51 | raise TypeError('List or Tuple entry needs to be a string')
52 | self.hdl_fileL.append(f)
53 |
54 | else:
55 | raise TypeError('files not string or list of strings')
56 |
57 | def createTcl(self, target, filename=None):
58 | '''Creat the TCL script
59 | '''
60 | if filename:
61 | fn = os.path.join(self.path, filename)
62 | else:
63 | fn = os.path.join(self.path, self.tcl_name)
64 |
65 | self.tcl_script += '# set compile directory:\n'
66 | self.tcl_script += 'set compile_directory %s\n'%self.path
67 | if self.top_name:
68 | self.tcl_script += 'set top_name %s\n'%self.top_name
69 | if self.hdl_fileL:
70 | self.tcl_script += '# input source files:\n'
71 | self.tcl_script += 'set hdl_files [ list \\\n'
72 | for f in self.hdl_fileL:
73 | self.tcl_script += ' '*17
74 | self.tcl_script += '%s \\\n'%f
75 | self.tcl_script += ']\n'
76 |
77 | if self.fpga:
78 | if not self.fpga.ucf_file:
79 | if not self.top_name:
80 | raise ValueError('No Top Name set')
81 | self.fpga.createUcf(self.top_name + '.ucf')
82 |
83 | self.tcl_script += '# set ucf file:\n'
84 | self.tcl_script += 'set constraints_file %s\n'% self.fpga.ucf_file
85 |
86 | self.tcl_script += '# set Project:\n'
87 | self.tcl_script += 'set proj %s\n'% self.top_name
88 |
89 | self.tcl_script += '# change to the directory:\n'
90 | self.tcl_script += 'cd %s\n'% self.path
91 |
92 | # test whether ise project file exits
93 | f = os.path.join(self.path, self.top_name + '.ise')
94 | pj_fn = self.top_name + '.ise'
95 | if os.path.exists(f):
96 | os.remove(f)
97 | self.tcl_script += '# set variables:\n'
98 | self.tcl_script += 'project new %s\n'%pj_fn
99 | if self.fpga.family:
100 | self.tcl_script += 'project set family %s\n'%self.fpga.family
101 | self.tcl_script += 'project set device %s\n'%self.fpga.device
102 | self.tcl_script += 'project set package %s\n'%self.fpga.package
103 | self.tcl_script += 'project set speed %s\n'%self.fpga.speed
104 |
105 | # add the hdl files
106 | self.tcl_script += '# add hdl files:\n'
107 | for hdl_file in self.hdl_fileL:
108 | self.tcl_script += 'xfile add %s\n'%hdl_file
109 |
110 | self.tcl_script += '# test if set_source_directory is set:\n'
111 | self.tcl_script += 'if { ! [catch {set source_directory'
112 | self.tcl_script += ' $source_directory}]} {\n'
113 | self.tcl_script += ' project set "Macro Search Path"\n'
114 | self.tcl_script += ' $source_directory -process Translate\n'
115 | self.tcl_script += '}\n'
116 |
117 |
118 | # run the implementation
119 | self.tcl_script += '# run the implementation:\n'
120 | self.tcl_script += 'process run "' + target + '"\n'
121 | # close the project
122 | self.tcl_script += '# close the project:\n'
123 | self.tcl_script += 'project close\n'
124 |
125 |
126 | fid = open(fn, 'w')
127 | fid.write(self.tcl_script)
128 | fid.close()
129 |
130 |
131 |
132 |
133 |
134 | def run(self, filename=None):
135 | '''Run the created TCL script
136 | '''
137 |
138 | # Workaround: Delete .xise file
139 | try:
140 | os.unlink(self.path + '/' + self.top_name + '.xise')
141 | except:
142 | pass
143 |
144 |
145 | if filename:
146 | tcl_name = filename
147 | else:
148 | tcl_name = os.path.join(self.path, self.tcl_name)
149 |
150 | if not os.path.exists(self.path):
151 | os.mkdir(self.path)
152 |
153 | # self.fpga.createUcf()
154 | cmd = 'xtclsh ' + tcl_name
155 | print 'running command: ', cmd
156 |
157 | import subprocess
158 | subprocess.call(cmd, shell=True)
159 |
160 | # r, w, e = popen2.popen3(cmd)
161 | # print 'errors: ', e.readlines()
162 | # print 'reply: ', r.readlines()
163 | # r.close()
164 | # e.close()
165 | # w.close()
166 |
167 | def setFpga(self, fpga):
168 | self.fpga = fpga
169 |
170 | #######################################################################
171 | #
172 | #
173 | class Fpga(object):
174 | def __init__(self, path=None):
175 | self.path= '.'
176 | if path is not None:
177 | self.path = path
178 | self.ucfD = {}
179 | self.ucf_file = None
180 | self.family = None
181 | self.device = None
182 | self.package = None
183 | self.speed = None
184 |
185 | def setPin(self, net, pin, iostandard=None, slew=None, drive=None):
186 | if iostandard is None and slew is None and drive is None:
187 | self.ucfD[pin] = [net, None]
188 | else:
189 | self.ucfD[pin] = [net, [iostandard, slew, drive]]
190 |
191 |
192 | def setDevice(self, family, device, package, speed):
193 |
194 | if not isinstance(family, str):
195 | raise TypeError('"Family" needs to be a string')
196 | if not isinstance(device, str):
197 | raise TypeError('"Device" needs to be a string')
198 | if not isinstance(speed, str):
199 | raise TypeError('"Speed" needs to be a string')
200 | if not isinstance(package, str):
201 | raise TypeError('"Package" needs to be a string')
202 |
203 | self.family = family
204 | self.device = device
205 | self.package = package
206 | self.speed = speed
207 |
208 |
209 |
210 | def createUcf(self, filename='my.ucf'):
211 | self.ucf_file = os.path.join(self.path, filename)
212 |
213 | str = '# UCF file automatically create by "%s"\n'%os.path.basename(sys.argv[0])
214 | str += '#\n'
215 | for key, value in self.ucfD.items():
216 | str += 'NET "%s" LOC = "%s"'%(value[0], key)
217 | if value[1] is not None:
218 | if value[1][0] is not None:
219 | str += ' | IOSTANDARD = %s'%value[1][0]
220 | if value[1][1] is not None:
221 | str += ' | SLEW = %s'%value[1][1]
222 | if value[1][2] is not None:
223 | str += ' | DRIVE = %s'%value[1][2]
224 |
225 | str += ' ;\n'
226 |
227 | str += '#\n'
228 |
229 | fid = open(self.ucf_file, 'w')
230 | fid.write(str)
231 | fid.close()
232 |
233 | def __repr__(self):
234 | s = 'FPGA:\n'
235 | if self.family:
236 | s += 'Family: %s\n'% self.family
237 | s += 'Device: %s\n'% self.device
238 | s += 'Package: %s\n'% self.package
239 | s += 'Speed: %s\n'% self.speed
240 | return s
241 |
--------------------------------------------------------------------------------
/thesis.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/strfry/nesfpga/5059f4b2394c47a59518675b1a63adfc50ff1b63/thesis.pdf
--------------------------------------------------------------------------------
/tools/cartridge.py:
--------------------------------------------------------------------------------
1 | from myhdl import *
2 |
3 | from file_rom import file_rom
4 |
5 | def str2bin(s):
6 | return tuple(map(lambda c: ord(c), s))
7 |
8 | rom_file = file("smb.nes")
9 | rom_header = rom_file.read(16)
10 | rom_prg = str2bin(rom_file.read(2**15))
11 | rom_chr = str2bin(rom_file.read(2**13))
12 |
13 | def CartridgeROM(
14 | CLK,
15 | RSTN,
16 | PRG_Address,
17 | PRG_Data,
18 | CHR_Address,
19 | CHR_Data
20 | ):
21 |
22 | PRG = file_rom(CLK, PRG_Address, PRG_Data, rom_prg)
23 | CHR = file_rom(CLK, CHR_Address, CHR_Data, rom_chr)
24 |
25 | return PRG, CHR
26 |
27 |
28 |
29 | def convert():
30 | clk = Signal(bool())
31 | rstn = Signal(bool())
32 | prg_address = Signal(intbv()[15:0])
33 | prg_data = Signal(intbv()[8:0])
34 | chr_address = Signal(intbv()[14:0])
35 | chr_data = Signal(intbv()[8:0])
36 |
37 | toVerilog(CartridgeROM, clk, rstn, prg_address, prg_data, chr_address, chr_data)
38 |
39 |
40 |
41 | convert()
42 |
--------------------------------------------------------------------------------
/tools/fbview/SDLMain.h:
--------------------------------------------------------------------------------
1 | /* SDLMain.m - main entry point for our Cocoa-ized SDL app
2 | Initial Version: Darrell Walisser
3 | Non-NIB-Code & other changes: Max Horn
4 |
5 | Feel free to customize this file to suit your needs
6 | */
7 |
8 | #ifndef _SDLMain_h_
9 | #define _SDLMain_h_
10 |
11 | #import
12 |
13 | @interface SDLMain : NSObject
14 | @end
15 |
16 | #endif /* _SDLMain_h_ */
17 |
--------------------------------------------------------------------------------
/tools/fbview/SDLMain.m:
--------------------------------------------------------------------------------
1 | /* SDLMain.m - main entry point for our Cocoa-ized SDL app
2 | Initial Version: Darrell Walisser
3 | Non-NIB-Code & other changes: Max Horn
4 |
5 | Feel free to customize this file to suit your needs
6 | */
7 |
8 | #include "SDL/SDL.h"
9 | #include "SDLMain.h"
10 | #include /* for MAXPATHLEN */
11 | #include
12 |
13 | /* For some reaon, Apple removed setAppleMenu from the headers in 10.4,
14 | but the method still is there and works. To avoid warnings, we declare
15 | it ourselves here. */
16 | @interface NSApplication(SDL_Missing_Methods)
17 | - (void)setAppleMenu:(NSMenu *)menu;
18 | @end
19 |
20 | /* Use this flag to determine whether we use SDLMain.nib or not */
21 | #define SDL_USE_NIB_FILE 0
22 |
23 | /* Use this flag to determine whether we use CPS (docking) or not */
24 | #define SDL_USE_CPS 1
25 | #ifdef SDL_USE_CPS
26 | /* Portions of CPS.h */
27 | typedef struct CPSProcessSerNum
28 | {
29 | UInt32 lo;
30 | UInt32 hi;
31 | } CPSProcessSerNum;
32 |
33 | extern OSErr CPSGetCurrentProcess( CPSProcessSerNum *psn);
34 | extern OSErr CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
35 | extern OSErr CPSSetFrontProcess( CPSProcessSerNum *psn);
36 |
37 | #endif /* SDL_USE_CPS */
38 |
39 | static int gArgc;
40 | static char **gArgv;
41 | static BOOL gFinderLaunch;
42 | static BOOL gCalledAppMainline = FALSE;
43 |
44 | static NSString *getApplicationName(void)
45 | {
46 | const NSDictionary *dict;
47 | NSString *appName = 0;
48 |
49 | /* Determine the application name */
50 | dict = (const NSDictionary *)CFBundleGetInfoDictionary(CFBundleGetMainBundle());
51 | if (dict)
52 | appName = [dict objectForKey: @"CFBundleName"];
53 |
54 | if (![appName length])
55 | appName = [[NSProcessInfo processInfo] processName];
56 |
57 | return appName;
58 | }
59 |
60 | #if SDL_USE_NIB_FILE
61 | /* A helper category for NSString */
62 | @interface NSString (ReplaceSubString)
63 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString;
64 | @end
65 | #endif
66 |
67 | @interface NSApplication (SDLApplication)
68 | @end
69 |
70 | @implementation NSApplication (SDLApplication)
71 | /* Invoked from the Quit menu item */
72 | - (void)terminate:(id)sender
73 | {
74 | /* Post a SDL_QUIT event */
75 | SDL_Event event;
76 | event.type = SDL_QUIT;
77 | SDL_PushEvent(&event);
78 | }
79 | @end
80 |
81 | /* The main class of the application, the application's delegate */
82 | @implementation SDLMain
83 |
84 | /* Set the working directory to the .app's parent directory */
85 | - (void) setupWorkingDirectory:(BOOL)shouldChdir
86 | {
87 | if (shouldChdir)
88 | {
89 | char parentdir[MAXPATHLEN];
90 | CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
91 | CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
92 | if (CFURLGetFileSystemRepresentation(url2, 1, (UInt8 *)parentdir, MAXPATHLEN)) {
93 | chdir(parentdir); /* chdir to the binary app's parent */
94 | }
95 | CFRelease(url);
96 | CFRelease(url2);
97 | }
98 | }
99 |
100 | #if SDL_USE_NIB_FILE
101 |
102 | /* Fix menu to contain the real app name instead of "SDL App" */
103 | - (void)fixMenu:(NSMenu *)aMenu withAppName:(NSString *)appName
104 | {
105 | NSRange aRange;
106 | NSEnumerator *enumerator;
107 | NSMenuItem *menuItem;
108 |
109 | aRange = [[aMenu title] rangeOfString:@"SDL App"];
110 | if (aRange.length != 0)
111 | [aMenu setTitle: [[aMenu title] stringByReplacingRange:aRange with:appName]];
112 |
113 | enumerator = [[aMenu itemArray] objectEnumerator];
114 | while ((menuItem = [enumerator nextObject]))
115 | {
116 | aRange = [[menuItem title] rangeOfString:@"SDL App"];
117 | if (aRange.length != 0)
118 | [menuItem setTitle: [[menuItem title] stringByReplacingRange:aRange with:appName]];
119 | if ([menuItem hasSubmenu])
120 | [self fixMenu:[menuItem submenu] withAppName:appName];
121 | }
122 | }
123 |
124 | #else
125 |
126 | static void setApplicationMenu(void)
127 | {
128 | /* warning: this code is very odd */
129 | NSMenu *appleMenu;
130 | NSMenuItem *menuItem;
131 | NSString *title;
132 | NSString *appName;
133 |
134 | appName = getApplicationName();
135 | appleMenu = [[NSMenu alloc] initWithTitle:@""];
136 |
137 | /* Add menu items */
138 | title = [@"About " stringByAppendingString:appName];
139 | [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
140 |
141 | [appleMenu addItem:[NSMenuItem separatorItem]];
142 |
143 | title = [@"Hide " stringByAppendingString:appName];
144 | [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
145 |
146 | menuItem = (NSMenuItem *)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
147 | [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
148 |
149 | [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
150 |
151 | [appleMenu addItem:[NSMenuItem separatorItem]];
152 |
153 | title = [@"Quit " stringByAppendingString:appName];
154 | [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
155 |
156 |
157 | /* Put menu into the menubar */
158 | menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
159 | [menuItem setSubmenu:appleMenu];
160 | [[NSApp mainMenu] addItem:menuItem];
161 |
162 | /* Tell the application object that this is now the application menu */
163 | [NSApp setAppleMenu:appleMenu];
164 |
165 | /* Finally give up our references to the objects */
166 | [appleMenu release];
167 | [menuItem release];
168 | }
169 |
170 | /* Create a window menu */
171 | static void setupWindowMenu(void)
172 | {
173 | NSMenu *windowMenu;
174 | NSMenuItem *windowMenuItem;
175 | NSMenuItem *menuItem;
176 |
177 | windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
178 |
179 | /* "Minimize" item */
180 | menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
181 | [windowMenu addItem:menuItem];
182 | [menuItem release];
183 |
184 | /* Put menu into the menubar */
185 | windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
186 | [windowMenuItem setSubmenu:windowMenu];
187 | [[NSApp mainMenu] addItem:windowMenuItem];
188 |
189 | /* Tell the application object that this is now the window menu */
190 | [NSApp setWindowsMenu:windowMenu];
191 |
192 | /* Finally give up our references to the objects */
193 | [windowMenu release];
194 | [windowMenuItem release];
195 | }
196 |
197 | /* Replacement for NSApplicationMain */
198 | static void CustomApplicationMain (int argc, char **argv)
199 | {
200 | NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
201 | SDLMain *sdlMain;
202 |
203 | /* Ensure the application object is initialised */
204 | [NSApplication sharedApplication];
205 |
206 | #ifdef SDL_USE_CPS
207 | {
208 | CPSProcessSerNum PSN;
209 | /* Tell the dock about us */
210 | if (!CPSGetCurrentProcess(&PSN))
211 | if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
212 | if (!CPSSetFrontProcess(&PSN))
213 | [NSApplication sharedApplication];
214 | }
215 | #endif /* SDL_USE_CPS */
216 |
217 | /* Set up the menubar */
218 | [NSApp setMainMenu:[[NSMenu alloc] init]];
219 | setApplicationMenu();
220 | setupWindowMenu();
221 |
222 | /* Create SDLMain and make it the app delegate */
223 | sdlMain = [[SDLMain alloc] init];
224 | [NSApp setDelegate:sdlMain];
225 |
226 | /* Start the main event loop */
227 | [NSApp run];
228 |
229 | [sdlMain release];
230 | [pool release];
231 | }
232 |
233 | #endif
234 |
235 |
236 | /*
237 | * Catch document open requests...this lets us notice files when the app
238 | * was launched by double-clicking a document, or when a document was
239 | * dragged/dropped on the app's icon. You need to have a
240 | * CFBundleDocumentsType section in your Info.plist to get this message,
241 | * apparently.
242 | *
243 | * Files are added to gArgv, so to the app, they'll look like command line
244 | * arguments. Previously, apps launched from the finder had nothing but
245 | * an argv[0].
246 | *
247 | * This message may be received multiple times to open several docs on launch.
248 | *
249 | * This message is ignored once the app's mainline has been called.
250 | */
251 | - (BOOL)application:(NSApplication *)theApplication openFile:(NSString *)filename
252 | {
253 | const char *temparg;
254 | size_t arglen;
255 | char *arg;
256 | char **newargv;
257 |
258 | if (!gFinderLaunch) /* MacOS is passing command line args. */
259 | return FALSE;
260 |
261 | if (gCalledAppMainline) /* app has started, ignore this document. */
262 | return FALSE;
263 |
264 | temparg = [filename UTF8String];
265 | arglen = SDL_strlen(temparg) + 1;
266 | arg = (char *) SDL_malloc(arglen);
267 | if (arg == NULL)
268 | return FALSE;
269 |
270 | newargv = (char **) realloc(gArgv, sizeof (char *) * (gArgc + 2));
271 | if (newargv == NULL)
272 | {
273 | SDL_free(arg);
274 | return FALSE;
275 | }
276 | gArgv = newargv;
277 |
278 | SDL_strlcpy(arg, temparg, arglen);
279 | gArgv[gArgc++] = arg;
280 | gArgv[gArgc] = NULL;
281 | return TRUE;
282 | }
283 |
284 |
285 | /* Called when the internal event loop has just started running */
286 | - (void) applicationDidFinishLaunching: (NSNotification *) note
287 | {
288 | int status;
289 |
290 | /* Set the working directory to the .app's parent directory */
291 | [self setupWorkingDirectory:gFinderLaunch];
292 |
293 | #if SDL_USE_NIB_FILE
294 | /* Set the main menu to contain the real app name instead of "SDL App" */
295 | [self fixMenu:[NSApp mainMenu] withAppName:getApplicationName()];
296 | #endif
297 |
298 | /* Hand off to main application code */
299 | gCalledAppMainline = TRUE;
300 | status = SDL_main (gArgc, gArgv);
301 |
302 | /* We're done, thank you for playing */
303 | exit(status);
304 | }
305 | @end
306 |
307 |
308 | @implementation NSString (ReplaceSubString)
309 |
310 | - (NSString *)stringByReplacingRange:(NSRange)aRange with:(NSString *)aString
311 | {
312 | unsigned int bufferSize;
313 | unsigned int selfLen = [self length];
314 | unsigned int aStringLen = [aString length];
315 | unichar *buffer;
316 | NSRange localRange;
317 | NSString *result;
318 |
319 | bufferSize = selfLen + aStringLen - aRange.length;
320 | buffer = (unichar *)NSAllocateMemoryPages(bufferSize*sizeof(unichar));
321 |
322 | /* Get first part into buffer */
323 | localRange.location = 0;
324 | localRange.length = aRange.location;
325 | [self getCharacters:buffer range:localRange];
326 |
327 | /* Get middle part into buffer */
328 | localRange.location = 0;
329 | localRange.length = aStringLen;
330 | [aString getCharacters:(buffer+aRange.location) range:localRange];
331 |
332 | /* Get last part into buffer */
333 | localRange.location = aRange.location + aRange.length;
334 | localRange.length = selfLen - localRange.location;
335 | [self getCharacters:(buffer+aRange.location+aStringLen) range:localRange];
336 |
337 | /* Build output string */
338 | result = [NSString stringWithCharacters:buffer length:bufferSize];
339 |
340 | NSDeallocateMemoryPages(buffer, bufferSize);
341 |
342 | return result;
343 | }
344 |
345 | @end
346 |
347 |
348 |
349 | #ifdef main
350 | # undef main
351 | #endif
352 |
353 |
354 | /* Main entry point to executable - should *not* be SDL_main! */
355 | int main (int argc, char **argv)
356 | {
357 | /* Copy the arguments into a global variable */
358 | /* This is passed if we are launched by double-clicking */
359 | if ( argc >= 2 && strncmp (argv[1], "-psn", 4) == 0 ) {
360 | gArgv = (char **) SDL_malloc(sizeof (char *) * 2);
361 | gArgv[0] = argv[0];
362 | gArgv[1] = NULL;
363 | gArgc = 1;
364 | gFinderLaunch = YES;
365 | } else {
366 | int i;
367 | gArgc = argc;
368 | gArgv = (char **) SDL_malloc(sizeof (char *) * (argc+1));
369 | for (i = 0; i <= argc; i++)
370 | gArgv[i] = argv[i];
371 | gFinderLaunch = NO;
372 | }
373 |
374 | #if SDL_USE_NIB_FILE
375 | NSApplicationMain (argc, argv);
376 | #else
377 | CustomApplicationMain (argc, argv);
378 | #endif
379 | return 0;
380 | }
381 |
382 |
--------------------------------------------------------------------------------
/tools/fbview/build.sh:
--------------------------------------------------------------------------------
1 | g++ fbview.cpp SDLMain.m -framework SDL -framework Cocoa -g
2 |
--------------------------------------------------------------------------------
/tools/fbview/fbview.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 |
4 | #include
5 |
6 | //#undef main
7 |
8 | #define WIDTH 340
9 | #define HEIGHT 261
10 |
11 | int color_palette[] = {
12 | 0x7C7C7C, 0x0000FC, 0x0000BC, 0x4428BC, 0x940084, 0xA80020, 0xA81000, 0x881400,
13 | 0x503000, 0x007800, 0x006800, 0x005800, 0x004058, 0x000000, 0x000000, 0x000000,
14 | 0xBCBCBC, 0x0078F8, 0x0058F8, 0x6844FC, 0xD800CC, 0xE40058, 0xF83800, 0xE45C10,
15 | 0xAC7C00, 0x00B800, 0x00A800, 0x00A844, 0x008888, 0x000000, 0x000000, 0x000000,
16 | 0xF8F8F8, 0x3CBCFC, 0x6888FC, 0x9878F8, 0xF878F8, 0xF85898, 0xF87858, 0xFCA044,
17 | 0xF8B800, 0xB8F818, 0x58D854, 0x58F898, 0x00E8D8, 0x787878, 0x000000, 0x000000,
18 | 0xFCFCFC, 0xA4E4FC, 0xB8B8F8, 0xD8B8F8, 0xF8B8F8, 0xF8A4C0, 0xF0D0B0, 0xFCE0A8,
19 | 0xF8D878, 0xD8F878, 0xB8F8B8, 0xB8F8D8, 0x00FCFC, 0xF8D8F8, 0x000000, 0x000000
20 |
21 | };
22 |
23 | std::vector g_Frames;
24 |
25 | int g_currentFrame = 0;
26 |
27 |
28 | // GHDL seems to have the same binary format as ModelSim, but with an additional header
29 | void probe_header(FILE* file)
30 | {
31 | char* line = 0;
32 | size_t len;
33 | getline(&line, &len, file);
34 |
35 | if (line && strstr(line, "GHDL-BINARY-FILE")) {
36 | getline(&line, &len, file);
37 | } else {
38 | rewind(file);
39 | }
40 |
41 | free(line);
42 | }
43 |
44 | void load_frames(const char* filename)
45 | {
46 | FILE* file = fopen(filename, "rb");
47 |
48 | if (!file) {
49 | perror("fopen");
50 | exit(1);
51 | }
52 |
53 | probe_header(file);
54 |
55 | int img_len = WIDTH * HEIGHT * 6;
56 |
57 | char img_buf[img_len];
58 |
59 | while (fread(img_buf, 1, img_len, file) == img_len) {
60 | puts("found image");
61 |
62 | SDL_Surface* surface = SDL_CreateRGBSurface(SDL_SWSURFACE, WIDTH, HEIGHT, 32, 0xff0000, 0xff00, 0xff, 0);
63 | SDL_LockSurface(surface);
64 |
65 | for (int y = 0; y < HEIGHT; y++) {
66 | for (int x = 0; x < WIDTH; x++) {
67 | int pixelValue = 0;
68 | for (int bit = 0; bit < 6; bit++) {
69 | switch (img_buf[(y * WIDTH + x) * 6 + bit]) {
70 | // case 0:
71 | // TODO: Handle Unknown/Invalid bit value
72 | // break;
73 | case 2:
74 | // Zero - do nothing
75 | break;
76 | case 3:
77 | pixelValue += (1 << (5 - bit));
78 | break;
79 | default:
80 | printf("Unknown bit value %x\n", img_buf[(y * WIDTH + x) * 6 + bit]);
81 | }
82 | }
83 | // printf("Computed Pixel Value %x\n", pixelValue);
84 | int* pixels = (int*)surface->pixels;
85 | // pixels[y * surface->w + x] = pixelValue;
86 | pixels[(surface->h - 1 - y) * surface->w + (surface->w - x - 1)] = color_palette[pixelValue];
87 | }
88 | }
89 |
90 | SDL_UnlockSurface(surface);
91 | g_Frames.push_back(surface);
92 | }
93 |
94 | }
95 |
96 | void draw() {
97 | // SDL_
98 | SDL_Surface* video = SDL_GetVideoSurface();
99 |
100 | SDL_BlitSurface(g_Frames[g_currentFrame], 0, video, 0);
101 | }
102 |
103 | void updateTitle() {
104 | char title[256];
105 | snprintf(title, 256, "Frame %d of %lu", g_currentFrame + 1, g_Frames.size());
106 | SDL_WM_SetCaption(title, 0);
107 | }
108 |
109 | int main(int argc, char* argv[])
110 | {
111 | if (argc < 2) {
112 | puts("Usage: fbview simdump.out");
113 | exit(1);
114 | }
115 |
116 | SDL_Surface *screen;
117 | SDL_Event event;
118 |
119 | if (SDL_Init(SDL_INIT_VIDEO) < 0 ) return 1;
120 |
121 | SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
122 |
123 | if (!(screen = SDL_SetVideoMode(WIDTH, HEIGHT, 0, SDL_SWSURFACE | SDL_DOUBLEBUF)))
124 | {
125 | SDL_Quit();
126 | return 1;
127 | }
128 |
129 | load_frames(argv[1]);
130 |
131 | int lastTime = SDL_GetTicks();
132 |
133 | int running = 1;
134 | while(running)
135 | {
136 |
137 | while(SDL_PollEvent(&event))
138 | {
139 | switch (event.type)
140 | {
141 | case SDL_QUIT:
142 | running = 0;
143 | break;
144 | case SDL_KEYDOWN:
145 | switch (event.key.keysym.sym) {
146 | case SDLK_q:
147 | running = 0;
148 | break;
149 | case SDLK_LEFT:
150 | if (g_currentFrame > 0) {
151 | g_currentFrame--;
152 | } else {
153 | g_currentFrame = g_Frames.size() - 1;
154 | }
155 | updateTitle();
156 | break;
157 | case SDLK_RIGHT:
158 | if (g_currentFrame < g_Frames.size() - 1) {
159 | g_currentFrame++;
160 | } else {
161 | g_currentFrame = 0;
162 | }
163 |
164 | updateTitle();
165 | break;
166 | }
167 | }
168 |
169 | }
170 |
171 |
172 | int curTime = SDL_GetTicks();
173 |
174 | float dt = float(curTime - lastTime) / 1000.0;
175 | lastTime = curTime;
176 | draw();
177 | SDL_Flip(screen);
178 |
179 | SDL_Delay(15);
180 |
181 |
182 | }
183 | SDL_Quit();
184 |
185 | return 0;
186 | }
187 |
188 |
189 |
190 |
191 |
--------------------------------------------------------------------------------
/tools/file_rom.py:
--------------------------------------------------------------------------------
1 | from myhdl import *
2 |
3 | rom = tuple(map(lambda c: ord(c), file("smb.nes").read()))
4 | rom = rom[:8192]
5 |
6 | def file_rom(clk, address, data, CONTENT):
7 |
8 | @always(clk.posedge)
9 | def logic():
10 | data.next = CONTENT[int(address)]
11 |
12 | return logic
13 |
14 |
--------------------------------------------------------------------------------
/tools/romconv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import sys
4 |
5 | filename = sys.argv[1]
6 | basename = filename.split(".")[0]
7 |
8 | f = file(filename)
9 | header = f.read(4)
10 | assert(header == "NES\x1a")
11 | prg_size = ord(f.read(1))
12 | chr_size = ord(f.read(1))
13 | f.read(10)
14 |
15 | def write_coe(name, bytes):
16 | fo = file(name, "w")
17 | fo.write("memory_initialization_radix=16;\nmemory_initialization_vector=\n")
18 | for i in range(0, bytes - 1):
19 | fo.write(hex(ord(f.read(1)))[2:] + ",\n")
20 | fo.write(hex(ord(f.read(1)))[2:] + ";\n")
21 |
22 | def write_dat(name, bytes):
23 | fo = file(name, "w")
24 | for i in range(0, bytes):
25 | byte = f.read(1)
26 | fo.write('{0:08b}\n'.format(ord(byte)))
27 |
28 |
29 | write_dat(basename + "_prg.dat", 16384 * prg_size)
30 | #write_coe(basename + "_chr.coe", 8192 * chr_size)
31 |
32 | write_dat(basename + "_chr.dat", 8192 * chr_size)
33 |
--------------------------------------------------------------------------------