Becoming an FPGA Engineer

You can talk about almost anything that you want to on this board.

Moderator: Moderators

lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Becoming an FPGA Engineer

Post by lidnariq »

That's surprisingly nice plywood :P
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

You know things are getting real when it's on plywood. :wink:

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.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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.

prg_back.jpg
prg_front.jpg

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. :lol: )
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.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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:

Code: Select all

                         +--/\/\/--- CPLD data bus
                         |   133
NES       level          |
data <--> shifter <--> --+--/\/\/--- RAM data bus
bus         ^(DIR)       |   133
            |            |
         CPU R/W         +--/\/\/--- ROM data bus
                             133
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.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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. :D

Next will be getting the CPLD to do what all those jumpers are doing.

IMG_0130_a.jpg
IMG_0133_a.jpg
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: Becoming an FPGA Engineer

Post by lidnariq »

Begins to remind me of microcontroller project lab :)
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: Becoming an FPGA Engineer

Post by Oziphantom »

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.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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.
That is a good question. I am not there yet for intermediate stuff myself but would certainly benefit from best practice suggestions.

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.

demo_board_wired.jpg
demo_board_pinout.png
pin_planner.png
cpu_bdf.png
ppu_bdf.png
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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.

block.png

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;
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

Today I got AMROM working and playing Captain Skyhawk, by only changing the VHDL code:

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;
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.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

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?

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;
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.
User avatar
Quietust
Posts: 1920
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Becoming an FPGA Engineer

Post by Quietust »

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 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

Code: 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;
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.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.
User avatar
Ben Boldt
Posts: 1149
Joined: Tue Mar 22, 2016 8:27 pm
Location: Minnesota, USA

Re: Becoming an FPGA Engineer

Post by Ben Boldt »

Quietust wrote: Sat Jun 05, 2021 7:20 pm 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";).
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.
Quietust wrote: Sat Jun 05, 2021 7:20 pmI'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.
Oh wow, good catch! I will give it a try when I go back in tomorrow. Will let you know, thanks!
Post Reply