Becoming an FPGA Engineer
Moderator: Moderators
Re: Becoming an FPGA Engineer
That's surprisingly nice plywood
Re: Becoming an FPGA Engineer
You know things are getting real when it's on plywood.
I am just noticing I didn't put any of the extra PRG/CHR RAM address pins out to any headers anywhere... Both of those DIP-28 sockets are for 32kbyte RAMs. I am trying to plan this out so it doesn't get too cobbled up but that will probably be unavoidable.
Edit:
I also did not do any translation for /IRQ. I need an open drain for this, correct? I can add a FET later.
I am just noticing I didn't put any of the extra PRG/CHR RAM address pins out to any headers anywhere... Both of those DIP-28 sockets are for 32kbyte RAMs. I am trying to plan this out so it doesn't get too cobbled up but that will probably be unavoidable.
Edit:
I also did not do any translation for /IRQ. I need an open drain for this, correct? I can add a FET later.
Re: Becoming an FPGA Engineer
PRG board update, ~75% done wiring this one. I added those green and red pin headers so I can put convenient jumpers to 5V/GND. I also added ROM /OE and PRG RAM A13, A14. My RAM's don't have +CE, only /CE and /OE. Wish I would have realized that before putting a jumper to 5V next to /OE (which would have been +CE) but whatever.
I did this crude math to select the resistors, considering short circuit.
5V = 50mA * R
R = 100 ohms.
So I used 100 ohms on 5V CPU data bus. 50mA was just a guess but I feel better than a direct short with this.
3.3V = 25mA * R
R = 132 ohms
So I used 133 ohms where the CPLD can read 3.3V signals as inputs. I looked it up to find 25mA. This is just in case I drive an output into those with messed up CPLD code.
I use my same old wiring color scheme:
Orange = power rail
green = gnd
red = address
blue = data
yellow = control
purple = something nifty (none of that yet. )
white = audio
I brought a 2n7000 FET I plan to use for /IRQ, which will invert it.
And no joke, I am trying to do a CIC defeat on this NES and following something I did earlier on my PAL NES:
viewtopic.php?f=9&t=18947
I removed the CIC and realize I had brought a DIP-14 socket and should have been DIP-16. So I bring the correct socket today and realize I counted wrong and brought a DIP-18... Will be interesting to see what size I bring tomorrow.
I did this crude math to select the resistors, considering short circuit.
5V = 50mA * R
R = 100 ohms.
So I used 100 ohms on 5V CPU data bus. 50mA was just a guess but I feel better than a direct short with this.
3.3V = 25mA * R
R = 132 ohms
So I used 133 ohms where the CPLD can read 3.3V signals as inputs. I looked it up to find 25mA. This is just in case I drive an output into those with messed up CPLD code.
I use my same old wiring color scheme:
Orange = power rail
green = gnd
red = address
blue = data
yellow = control
purple = something nifty (none of that yet. )
white = audio
I brought a 2n7000 FET I plan to use for /IRQ, which will invert it.
And no joke, I am trying to do a CIC defeat on this NES and following something I did earlier on my PAL NES:
viewtopic.php?f=9&t=18947
I removed the CIC and realize I had brought a DIP-14 socket and should have been DIP-16. So I bring the correct socket today and realize I counted wrong and brought a DIP-18... Will be interesting to see what size I bring tomorrow.
Re: Becoming an FPGA Engineer
I realized a slight error I have made planning out the data bus resistors. Since the mapper will control the enables of RAM, ROM, and itself, each of those can conflict with each other. There won't be a conflict on the NES 5V version of the data bus because I have connected CPU R/W directly to the DIR of the level shifter, so that's not an issue.
I think I should hook it up like this to be safe:
This creates 133 ohms for conflict between any of them and the NES CPU. The 3 on the right should never intentionally talk to each other directly, so having 2x resistance on those paths should be great. I will have to get a little creative where to put those resistors but I think I can do it without any tombstoning.
I think I should hook it up like this to be safe:
Code: Select all
+--/\/\/--- CPLD data bus
| 133
NES level |
data <--> shifter <--> --+--/\/\/--- RAM data bus
bus ^(DIR) | 133
| |
CPU R/W +--/\/\/--- ROM data bus
133
Re: Becoming an FPGA Engineer
I have what is probably a really dumb question. I have completely wired up the PRG side now and I programmed the PRG-ROM with Castlequest. (That is my game of choice testing stuff because it makes an immediate loud sound when you turn it on.). Dead as a doornail. Castlequest is 32 kilobyte NROM. These are the connections I had made with jumpers:
CPU A13 -> PRG A13
CPU A14 -> PRG A14
/ROMSEL -> ROM /CE
GND -> ROM /OE, PRG A18,17,16,15
I removed my clone 72-pin female connector, put the ZIF back on, and verified the NES is good and plays a game. I then hooked up my logic analyzer and found that the ROM IS running, level shifted, bytes returned correctly comparing to ROM image. It is sitting in a loop waiting for PPU_STATUS ($2002) bit 7 to get set (i.e. waiting for V-blank). It looks like it is working! But the game does not produce any sound. I also tried Millipede (doubled up to fill 32k) in case Castlequest is strange for some reason, no difference. This is the same NES that had a Hi-Def NES explosion, but I removed that and again, the NES works fine with a real cartridge...
So here's my dumb question. With nothing connected to the PPU's pins of the cartridge connector (since I have not finished building that part yet), does something about that prevent the PPU from initializing correctly? I do get a solid color screen that is syncing when I turn on the power, and I observed it change to red as I cycled power. I was expecting open bus type graphics but now I am wondering if something relies on CHR-RAM or -ROM being there. There must just be something obvious like that happening because the CPU is executing apparently correctly.
CPU A13 -> PRG A13
CPU A14 -> PRG A14
/ROMSEL -> ROM /CE
GND -> ROM /OE, PRG A18,17,16,15
I removed my clone 72-pin female connector, put the ZIF back on, and verified the NES is good and plays a game. I then hooked up my logic analyzer and found that the ROM IS running, level shifted, bytes returned correctly comparing to ROM image. It is sitting in a loop waiting for PPU_STATUS ($2002) bit 7 to get set (i.e. waiting for V-blank). It looks like it is working! But the game does not produce any sound. I also tried Millipede (doubled up to fill 32k) in case Castlequest is strange for some reason, no difference. This is the same NES that had a Hi-Def NES explosion, but I removed that and again, the NES works fine with a real cartridge...
So here's my dumb question. With nothing connected to the PPU's pins of the cartridge connector (since I have not finished building that part yet), does something about that prevent the PPU from initializing correctly? I do get a solid color screen that is syncing when I turn on the power, and I observed it change to red as I cycled power. I was expecting open bus type graphics but now I am wondering if something relies on CHR-RAM or -ROM being there. There must just be something obvious like that happening because the CPU is executing apparently correctly.
Re: Becoming an FPGA Engineer
Okay, I know what the problem is now but I do not know a great way to fix it.
I have a data bus conflict from the level shifters. I have CPU R/W connected directly to the DIR pin and 0 connected to /CE. The problem with this is when the CPU tries to read anything, the level shifter drives the bus. I did connect /ROMSEL to my ROM /CE, so what that is doing is causing the level shifter to buffer the 3.3V open bus and driving the resulting garbage onto the 5V data bus. This will conflict with the NES's internal registers and RAM.
So what is a good solution to that? I can't just connect /ROMSEL to the level shifter's /OE because I might want PRG-RAM or registers at $4100, etc. I hadn't expected having to add logic for this; is there a simpler approach to this that has been done before?
Edit:
Works now when I feed /ROMSEL into the data bus level shifter's /OE. I put both the /OE and /ROMSEL out to a 2-pin header that I can stick a jumper onto, or remove it and control with the CPLD. The fact that it runs now also confirms that my zillion 100/133 ohm resistors can work and may have already done some good.
In hindsight, I may have been able to use 5V ROM and RAM and put just the mapper behind the level shifters. Then the /CE's would have worked like normal, directly affecting their presence on the CPU's data bus. Interesting design choice side-effect that I had not considered. Will drive on with the 3.3V parts I think. I think I can basically take the AND of the RAM and ROM /CE's and put that out to the level shifter's /OE.
I also can't lose track that this is a CPLD/FPGA learning tool first, not a cost-driven NES cartridge designer tool. This presents an opportunity to fix a hardware 'bug' with CPLD code down the road, so this is all good from that perspective.
I have a data bus conflict from the level shifters. I have CPU R/W connected directly to the DIR pin and 0 connected to /CE. The problem with this is when the CPU tries to read anything, the level shifter drives the bus. I did connect /ROMSEL to my ROM /CE, so what that is doing is causing the level shifter to buffer the 3.3V open bus and driving the resulting garbage onto the 5V data bus. This will conflict with the NES's internal registers and RAM.
So what is a good solution to that? I can't just connect /ROMSEL to the level shifter's /OE because I might want PRG-RAM or registers at $4100, etc. I hadn't expected having to add logic for this; is there a simpler approach to this that has been done before?
Edit:
Works now when I feed /ROMSEL into the data bus level shifter's /OE. I put both the /OE and /ROMSEL out to a 2-pin header that I can stick a jumper onto, or remove it and control with the CPLD. The fact that it runs now also confirms that my zillion 100/133 ohm resistors can work and may have already done some good.
In hindsight, I may have been able to use 5V ROM and RAM and put just the mapper behind the level shifters. Then the /CE's would have worked like normal, directly affecting their presence on the CPU's data bus. Interesting design choice side-effect that I had not considered. Will drive on with the 3.3V parts I think. I think I can basically take the AND of the RAM and ROM /CE's and put that out to the level shifter's /OE.
I also can't lose track that this is a CPLD/FPGA learning tool first, not a cost-driven NES cartridge designer tool. This presents an opportunity to fix a hardware 'bug' with CPLD code down the road, so this is all good from that perspective.
Re: Becoming an FPGA Engineer
I got everything finished and rigged up to play Millipede. Everything is working normally now with 3.3V memories. I even sanded and varnished the plywood.
Next will be getting the CPLD to do what all those jumpers are doing.
Next will be getting the CPLD to do what all those jumpers are doing.
Re: Becoming an FPGA Engineer
Begins to remind me of microcontroller project lab
-
- Posts: 1565
- Joined: Tue Feb 07, 2017 2:03 am
Re: Becoming an FPGA Engineer
on the topics of VHDL, does anybody have a good intermediate training source? I know how to make a shift register and other components, but I haven't found anything that covers best practices on putting them together, how to and when to using the timing constraint tools and other "not a trivial project" design.
Re: Becoming an FPGA Engineer
That is a good question. I am not there yet for intermediate stuff myself but would certainly benefit from best practice suggestions.Oziphantom wrote: ↑Thu Jun 03, 2021 1:48 am on the topics of VHDL, does anybody have a good intermediate training source? I know how to make a shift register and other components, but I haven't found anything that covers best practices on putting them together, how to and when to using the timing constraint tools and other "not a trivial project" design.
I got everything running through the CPLD instead of jumpers to run the NROM game Millipede now. I am always so amazed when I turn it on and it works... That's not saying much for my confidence level but it is pretty exciting. I have routed all the signals necessary for MMC1 and MMC3, and plenty of open pins left. I kept all the CPU/PRG stuff on side A and PPU/CHR stuff on side B of the CPLD. I am not really aware of any constraints using side A vs. side B, or crossing between them. I am pretty sure there are though, and I think this will be a fun thing to come back in the future and read.
So far, I have done everything in block diagram / schematic mode, no code yet necessary. As I move on to mappers with registers, I expect to be diving into writing code.
Re: Becoming an FPGA Engineer
I modified to use VHDL code instead of directly block diagram/schematic today. I tested it and it is working. With this approach, I can modify just the VHDL file to create different mappers.
Code: Select all
library ieee;
use ieee.std_logic_1164.all;
entity NROM_MAPPER is
port
(
-- CPU/PRG signals
cpu_data_bus : inout std_logic_vector(7 downto 0);
cpu_rw : in std_logic;
m2 : in std_logic;
cpu_address_bus : in std_logic_vector(15 downto 11);
cpu_a0 : in std_logic;
cpu_shifter_oe_n : out std_logic;
prg_ram_we_n : out std_logic;
prg_ram_ce_n : out std_logic;
prg_ram_oe_n : out std_logic;
prg_rom_ce_n : out std_logic;
prg_rom_address : out std_logic_vector(18 downto 13);
irq_inverted : out std_logic;
-- PPU/CHR Signals
ppu_rd_n : in std_logic;
ppu_wr_n : in std_logic;
ppu_address_bus : in std_logic_vector(13 downto 10);
ppu_shifter_oe_n : out std_logic;
chr_ram_we_n : out std_logic;
chr_ram_ce_n : out std_logic;
chr_ram_oe_n : out std_logic;
chr_rom_ce_n : out std_logic;
chr_rom_address : out std_logic_vector(18 downto 10);
ciram_a10 : out std_logic;
ciram_ce_n : out std_logic
);
end NROM_MAPPER;
architecture logic of NROM_MAPPER is
begin
-- CPU/PRG signals
cpu_shifter_oe_n <= cpu_address_bus(15);
prg_ram_we_n <= cpu_rw;
prg_ram_ce_n <= '1';
prg_ram_oe_n <= '1';
prg_rom_ce_n <= '0';
irq_inverted <= '0';
prg_rom_address <= (14=>cpu_address_bus(14), 13=>cpu_address_bus(13), others=>'0');
-- PPU/CHR Signals
ppu_shifter_oe_n <= ppu_address_bus(13);
chr_ram_we_n <= ppu_wr_n;
chr_ram_ce_n <= '1';
chr_ram_oe_n <= '1';
chr_rom_ce_n <= '0';
chr_rom_address <= (12=>ppu_address_bus(12), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0');
ciram_a10 <= ppu_address_bus(10); -- Vertical Mirroring
--ciram_a10 <= ppu_address_bus(11); -- Horizontal Mirroring
ciram_ce_n <= not ppu_address_bus(13);
end logic;
Re: Becoming an FPGA Engineer
Today I got AMROM working and playing Captain Skyhawk, by only changing the VHDL code:
This mapper has 1 register at $8000-FFFF, and uses 8kbytes CHR-RAM. I am using a signal to store the PRG ROM bank register value. Since this is a 128kbyte game, I burnt just 128k to the beginning of the ROM without repeats. So I had to explicitly set those upper PRG address bits to 0 because of this and not support the PRG A17 from register bit 2 as defined in the wiki.
It took me a bit to figure out that the register's bit 4 directly controls CIRAM A10. That was easy to hook up once I realized that.
Code: Select all
library ieee;
use ieee.std_logic_1164.all;
entity AXROM_MAPPER is
port
(
-- CPU/PRG signals
cpu_data_bus : inout std_logic_vector(7 downto 0);
cpu_rw : in std_logic;
m2 : in std_logic;
cpu_address_bus : in std_logic_vector(15 downto 11);
cpu_a0 : in std_logic;
cpu_shifter_oe_n : out std_logic;
prg_ram_we_n : out std_logic;
prg_ram_ce_n : out std_logic;
prg_ram_oe_n : out std_logic;
prg_rom_ce_n : out std_logic;
prg_rom_address : out std_logic_vector(18 downto 13);
irq_inverted : out std_logic;
-- PPU/CHR Signals
ppu_rd_n : in std_logic;
ppu_wr_n : in std_logic;
ppu_address_bus : in std_logic_vector(13 downto 10);
ppu_shifter_oe_n : out std_logic;
chr_ram_we_n : out std_logic;
chr_ram_ce_n : out std_logic;
chr_ram_oe_n : out std_logic;
chr_rom_ce_n : out std_logic;
chr_rom_address : out std_logic_vector(18 downto 10);
ciram_a10 : out std_logic;
ciram_ce_n : out std_logic
);
end AXROM_MAPPER;
architecture logic of AXROM_MAPPER is
signal prg_bank: std_logic_vector(2 downto 0);
begin
-- CPU/PRG signals
cpu_data_bus <= "ZZZZZZZZ"; -- Set data bus as input.
cpu_shifter_oe_n <= cpu_address_bus(15);
prg_ram_we_n <= cpu_rw;
prg_ram_ce_n <= '1'; -- Disable PRG-RAM
prg_ram_oe_n <= '1';
prg_rom_ce_n <= not cpu_rw; -- Prevent bus conflict writing to ROM.
irq_inverted <= '0';
prg_rom_address <= (16=>prg_bank(1), 15=>prg_bank(0), 14=>cpu_address_bus(14), 13=>cpu_address_bus(13), others=>'0');
-- Note: Add (17=>prg_bank(2)) for 256 kbyte game.
-- PPU/CHR Signals
ppu_shifter_oe_n <= ppu_address_bus(13);
chr_ram_we_n <= ppu_wr_n;
chr_ram_ce_n <= ppu_address_bus(13);
chr_ram_oe_n <= '0';
chr_rom_ce_n <= '1'; -- disable CHR-ROM.
chr_rom_address <= (12=>ppu_address_bus(12), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0');
--ciram_a10 <= ppu_address_bus(10); -- Vertical Mirroring
--ciram_a10 <= ppu_address_bus(11); -- Horizontal Mirroring
ciram_ce_n <= not ppu_address_bus(13);
-- Writable Register
prg_bank <= cpu_data_bus(2 downto 0) when ( m2'event and (m2 = '0') and (cpu_rw = '0') and (cpu_address_bus(15) = '0') );
ciram_a10 <= cpu_data_bus(4) when ( m2'event and (m2 = '0') and (cpu_rw = '0') and (cpu_address_bus(15) = '0') );
end logic;
It took me a bit to figure out that the register's bit 4 directly controls CIRAM A10. That was easy to hook up once I realized that.
Re: Becoming an FPGA Engineer
I took a bit of a big jump to MMC1. I probably should have tried something easier first... I have Rad Racer burnt to the ROM chip, and this time I did repeat it to fill the whole chip. (128kbyte repeated 4x to fill my 512kbyte ROM.) This game uses CHR-RAM. I have it sort-of working. If I turn on the NES from a cold start, the CPU runs fine and I can play the game. (It won't run directly after programming for some reason, I have to power cycle the CPLD...) The PPU seems to have the correct nametables and attribute tables (i.e. ciram seems OK) but I am getting strangeness on my pattern tables. Some graphics are close to correct but have lots of flickering pixels. Other graphics seem to show incorrect graphics. The flickeryness tells me that there is a problem reading from the CHR-RAM. The wrong graphics tells me that there is either a problem writing to the CHR-RAM or a problem with CHR-RAM banking.
Would anyone be so nice to check my code for obvious errors?
Also I am curious, I added several 'process' sections for items that are fully combinational, because it was more familiar to me to use the if/elsif/else structure instead of using when/else statements. Where I am loading registers, that one probably needs to be a process because it waits for M2 falling edge for each step. Is there any disadvantage of using processes for things that are totally combinational? I am wondering if it wastes resources or if the compiler is smart enough to treat it the same. Or if that is bad practice. Or good practice, I really don't know but would like to form good habits.
Any comments and suggestions would be appreciated.
Would anyone be so nice to check my code for obvious errors?
Code: Select all
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity MMC1_MAPPER is
port
(
-- CPU/PRG signals
cpu_data_bus : inout std_logic_vector(7 downto 0);
cpu_rw : in std_logic;
m2 : in std_logic;
cpu_address_bus : in std_logic_vector(15 downto 11);
cpu_a0 : in std_logic;
cpu_shifter_oe_n : out std_logic;
prg_ram_we_n : out std_logic;
prg_ram_ce_n : out std_logic;
prg_ram_oe_n : out std_logic;
prg_rom_ce_n : out std_logic;
prg_rom_address : out std_logic_vector(18 downto 13);
irq_inverted : out std_logic;
-- PPU/CHR Signals
ppu_rd_n : in std_logic;
ppu_wr_n : in std_logic;
ppu_address_bus : in std_logic_vector(13 downto 10);
ppu_shifter_oe_n : out std_logic;
chr_ram_we_n : out std_logic;
chr_ram_ce_n : out std_logic;
chr_ram_oe_n : out std_logic;
chr_rom_ce_n : out std_logic;
chr_rom_address : out std_logic_vector(18 downto 10);
ciram_a10 : out std_logic;
ciram_ce_n : out std_logic
);
end MMC1_MAPPER;
architecture logic of MMC1_MAPPER is
signal control_reg: std_logic_vector(4 downto 0) := "11111"; -- $8000-9FFF
signal chr_bank_0_reg: std_logic_vector(4 downto 0); -- $A000-BFFF
signal chr_bank_1_reg: std_logic_vector(4 downto 0); -- $C000-DFFF
signal prg_bank_reg: std_logic_vector(4 downto 0); -- $E000-FFFF
signal reg_shift: std_logic_vector(3 downto 0);
signal reg_bit_counter: integer := 0;
begin
-- CPU/PRG signals
cpu_data_bus <= "ZZZZZZZZ"; -- Set data bus as input.
cpu_shifter_oe_n <= cpu_address_bus(15);
prg_ram_we_n <= cpu_rw;
prg_ram_ce_n <= '1'; -- Disable PRG-RAM
prg_ram_oe_n <= '1';
prg_rom_ce_n <= not cpu_rw; -- Prevent bus conflict writing to ROM.
irq_inverted <= '0';
-- PPU/CHR Signals
ppu_shifter_oe_n <= ppu_address_bus(13);
chr_ram_we_n <= ppu_wr_n;
chr_ram_ce_n <= ppu_address_bus(13);
chr_ram_oe_n <= '0';
chr_rom_ce_n <= '1'; -- disable CHR-ROM.
ciram_ce_n <= not ppu_address_bus(13);
-- Handle Register Writes:
process( m2, cpu_rw, cpu_address_bus, reg_bit_counter, control_reg, prg_bank_reg )
begin
if (m2'event) and (m2='0') and (cpu_rw='0') and (cpu_address_bus(15)='0') then
if cpu_data_bus(7) = '1' then -- Reset detected by CPU D7 = 1.
reg_bit_counter <= 0;
elsif reg_bit_counter < 4 then -- Accumulating bits.
reg_shift <= cpu_data_bus(0) & reg_shift(3 downto 1);
reg_bit_counter <= reg_bit_counter + 1;
elsif cpu_address_bus(14 downto 13) = "00" then -- $8000-9FFF
control_reg <= cpu_data_bus(0) & reg_shift;
reg_bit_counter <= 0;
elsif cpu_address_bus(14 downto 13) = "01" then -- $A000-BFFF
chr_bank_0_reg <= cpu_data_bus(0) & reg_shift;
reg_bit_counter <= 0;
elsif cpu_address_bus(14 downto 13) = "10" then -- $C000-DFFF
chr_bank_1_reg <= cpu_data_bus(0) & reg_shift;
reg_bit_counter <= 0;
elsif cpu_address_bus(14 downto 13) = "11" then -- $E000-FFFF
prg_bank_reg <= cpu_data_bus(0) & reg_shift;
reg_bit_counter <= 0;
end if;
end if;
end process;
-- Handle PRG Banking:
process( cpu_address_bus, control_reg, prg_bank_reg )
begin
if control_reg(3) = '0' then -- 32kbyte bank mode
prg_rom_address <= (17=>prg_bank_reg(3), 16=>prg_bank_reg(2), 15=>prg_bank_reg(1), 14=>cpu_address_bus(14), 13=>cpu_address_bus(13), others=>'0');
elsif control_reg(2) = '0' then -- 16kbyte bank mode, fix first bank at $8000-BFFF.
if( cpu_address_bus(14) = '0' ) then -- CPU is accessing $8000-BFFF.
prg_rom_address <= (17=>'0', 16=>'0', 15=>'0', 14=>'0', 13=>cpu_address_bus(13), others=>'0');
else -- CPU is accessing last bank at $C000-FFFF.
prg_rom_address <= (17=>prg_bank_reg(3), 16=>prg_bank_reg(2), 15=>prg_bank_reg(1), 14=>prg_bank_reg(0), 13=>cpu_address_bus(13), others=>'0');
end if;
else -- 16kbyte bank mode, fix last bank at $C000.
if( cpu_address_bus(14) = '0' ) then -- CPU is accessing $8000-BFFF.
prg_rom_address <= (17=>prg_bank_reg(3), 16=>prg_bank_reg(2), 15=>prg_bank_reg(1), 14=>prg_bank_reg(0), 13=>cpu_address_bus(13), others=>'0');
else -- CPU is accessing last bank at $C000-FFFF.
prg_rom_address <= (17=>'1', 16=>'1', 15=>'1', 14=>'1', 13=>cpu_address_bus(13), others=>'0');
end if;
end if;
end process;
-- Handle CHR Banking:
process( ppu_address_bus, control_reg, chr_bank_0_reg, chr_bank_1_reg )
begin
if control_reg(4) = '0' then -- 1 x 8kbyte mode
chr_rom_address <= (15=>chr_bank_0_reg(3), 14=>chr_bank_0_reg(2), 13=>chr_bank_0_reg(1), 12=>ppu_address_bus(12), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0');
else -- 2 x 4kbyte mode
if ppu_address_bus(12)='0' then -- low bank
chr_rom_address <= (15=>chr_bank_0_reg(3), 14=>chr_bank_0_reg(2), 13=>chr_bank_0_reg(1), 12=>chr_bank_0_reg(0), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0');
else -- high bank
chr_rom_address <= (15=>chr_bank_1_reg(3), 14=>chr_bank_1_reg(2), 13=>chr_bank_1_reg(1), 12=>chr_bank_1_reg(0), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0');
end if;
end if;
end process;
-- Handle Mirroring:
process( ppu_address_bus, control_reg )
begin
if control_reg(1 downto 0) = "00" then -- one-screen, lower bank
ciram_a10 <= '0';
elsif control_reg(1 downto 0) = "01" then -- one-screen, upper bank
ciram_a10 <= '1';
elsif control_reg(1 downto 0) = "10" then -- Vertical Mirroring
ciram_a10 <= ppu_address_bus(10);
else -- Horizontal Mirroring
ciram_a10 <= ppu_address_bus(11);
end if;
end process;
end logic;
Any comments and suggestions would be appreciated.
Re: Becoming an FPGA Engineer
I see one minor issue: writing with D7=1 is supposed to also set both of the PRG bank mode bits (i.e. something along the lines of control_reg(3 downto 2) <= "11";).Ben Boldt wrote: ↑Sat Jun 05, 2021 6:44 pm Would anyone be so nice to check my code for obvious errors?
Code: Select all
-- Handle Register Writes: process( m2, cpu_rw, cpu_address_bus, reg_bit_counter, control_reg, prg_bank_reg ) begin if (m2'event) and (m2='0') and (cpu_rw='0') and (cpu_address_bus(15)='0') then if cpu_data_bus(7) = '1' then -- Reset detected by CPU D7 = 1. reg_bit_counter <= 0; elsif reg_bit_counter < 4 then -- Accumulating bits. ...
I'm pretty sure those should be using all 5 bits of the register, sending the upper bit to CHR A16, otherwise it'll only be able to access 64KB instead of 128KB.Ben Boldt wrote: ↑Sat Jun 05, 2021 6:44 pmCode: Select all
-- Handle CHR Banking: process( ppu_address_bus, control_reg, chr_bank_0_reg, chr_bank_1_reg ) begin if control_reg(4) = '0' then -- 1 x 8kbyte mode chr_rom_address <= (15=>chr_bank_0_reg(3), 14=>chr_bank_0_reg(2), 13=>chr_bank_0_reg(1), 12=>ppu_address_bus(12), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0'); else -- 2 x 4kbyte mode if ppu_address_bus(12)='0' then -- low bank chr_rom_address <= (15=>chr_bank_0_reg(3), 14=>chr_bank_0_reg(2), 13=>chr_bank_0_reg(1), 12=>chr_bank_0_reg(0), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0'); else -- high bank chr_rom_address <= (15=>chr_bank_1_reg(3), 14=>chr_bank_1_reg(2), 13=>chr_bank_1_reg(1), 12=>chr_bank_1_reg(0), 11=>ppu_address_bus(11), 10=>ppu_address_bus(10), others=>'0'); end if; end if; end process;
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
P.S. If you don't get this note, let me know and I'll write you another.
Re: Becoming an FPGA Engineer
Okay, I will give that a try. I see what you are talking about in the wiki now as well. That may explain why I have to cycle power to the CPLD.
Oh wow, good catch! I will give it a try when I go back in tomorrow. Will let you know, thanks!