MMC1 with less than 48/40 macrocells?ideal 36MC possible!!!

Discuss hardware-related topics, such as development cartridges, CopyNES, PowerPak, EPROMs, or whatever.

Moderator: Moderators

Post Reply
darleiv
Posts: 69
Joined: Wed Feb 23, 2022 10:55 pm

MMC1 with less than 48/40 macrocells?ideal 36MC possible!!!

Post by darleiv »

the challenge would be to do it using 36 macrocells
what do you think this code is able to work?
take away this doubt
and contains some errors
this code uses 48 macrocells
M2 = 1
A15 = 0
ROMSEL = 1?




--Signal types are listed in parenthesis:
--
--(r) this line goes to the ROM only.
--(s) this line is Shared between the ROM, MMC/chip, and Nintendo
--(n) this line connects to the NES cart edge only, and not the ROM
--(w) this line connects to the WRAM only and nowhere else
--
--
--MMC1 Chip: (24 pin shrink-DIP)
------------
--Comes in several varieties: 'MMC1', 'MMC1A', and 'MMC1B2'
--
-- .---\/---.
-- PRG A14 (r) - |01 24| - +5V
-- PRG A15 (r) - |02 23| - M2
-- PRG A16 (r) - |03 22| - PRG A13 (s)
-- PRG A17 (r) - |04 21| - PRG A14 (n)
-- PRG /CE (r) - |05 20| - PRG /CE (n)
-- WRAM CE (w) - |06 19| - PRG D7 (s)
-- CHR A12 (r) - |07 18| - PRG D0 (s)
-- CHR A13 (r) - |08 17| - PRG R/W
-- CHR A14 (r) - |09 16| - CIRAM A10 (n)
-- CHR A15 (r) - |10 15| - CHR A12 (n)
-- CHR A16 (r) or WRAM /CE (w) - |11 14| - CHR A11 (s)
-- GND - |12 13| - CHR A10 (s)
-- `--------'
--
-- MMC1
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
use IEEE.NUMERIC_STD.ALL;

library altera;
use altera.altera_primitives_components.all;

entity MMC1 is

port (
--reset generator (if required)
--note there are ports specified here which are not required
--for MMC1, this is because the PCB will support other mappers as
--well
nRST_p : in std_logic;

--input from NES
CPUDATA_p : in std_logic_vector(7 downto 0);
CPURnW_p : in std_logic;
nROMSEL_p : in std_logic;
CPUA14_p : in std_logic;
CPUA13_p : in std_logic;
CPUA0_p : in std_logic;
nPPUA13_p : in std_logic;
PPUA13_p : in std_logic;
PPUA12_p : in std_logic;
PPUA11_p : in std_logic;
PPUA10_p : in std_logic;
nPPURD_p : in std_logic;
nPPUWR_p : in std_logic;
M2_p : in std_logic;
CLK_p : in std_logic;

--output to Program ROM / WRAM
PRGA18_p : out std_logic;
PRGA17_p : out std_logic;
PRGA16_p : out std_logic;
PRGA15_p : out std_logic;
PRGA14_p : out std_logic;
PRGA13_p : out std_logic;
nPRGCE_p : out std_logic;
nWRAMCE_p : out std_logic;

--output to Character ROM
CHRA17_p : out std_logic;
CHRA16_p : out std_logic;
CHRA15_p : out std_logic;
CHRA14_p : out std_logic;
CHRA13_p : out std_logic;
CHRA12_p : out std_logic;
CHRA11_p : out std_logic;
CHRA10_p : out std_logic;

--output to NES
nIRQ_p : out std_logic;
nCIRAMCE_p : out std_logic;
CIRAMA10_p : out std_logic
);
end entity;

architecture MMC1_a of MMC1 is

signal RomAddr17to14_s : std_logic_vector(3 downto 0);
signal ChrAddr16to12_s : std_logic_vector(4 downto 0);
signal cpuA15_s : std_logic;

signal MMCReg0_s : std_logic_vector(4 downto 0);
signal MMCReg1_s : std_logic_vector(4 downto 0);
signal MMCReg2_s : std_logic_vector(4 downto 0);
signal MMCReg3_s : std_logic_vector(4 downto 0);
signal TempReg_s : std_logic_vector(4 downto 0);
signal CHRMirr_s : std_logic_vector(1 downto 0);

--state machine for serial writes to registers
signal resetState : std_logic;
type state_type is (s0,s1,s2,s3,s4);
signal current_s,next_s : state_type;
signal cpuAddr15to13_s : std_logic_vector(2 downto 0);

begin

--no IRQ in MMC1
nIRQ_p <= 'Z';

--CIRAM always enabled
nCIRAMCE_p <= nPPUA13_p;

--determine A15
cpuA15_s <= '1' when (M2_p = '1' and nROMSEL_p = '0') else '1';

--group higher addresses for easier reading
cpuAddr15to13_s <= cpuA15_s & CPUA14_p & CPUA13_p;

--**************************************************************
--WRAM
--CPU $6000-$7FFF: 8 KB PRG RAM bank, fixed on all boards but SOROM and SXROM
--M2 is high when address is valid
--0b0110 -> 0b0111
nWRAMCE_p <= '0' when (M2_p = '1' and cpuAddr15to13_s = "011" and MMCReg3_s(4) = '0') else '1';

--**************************************************************
--Mirroring
--To configure a cartridge board for horizontal mirroring, connect PPU A11 to CIRAM A10
--To configure a cartridge board for vertical mirroring, connect PPU A10 to CIRAM A10
--00b - 1-screen mirroring (nametable 0)
--01b - 1-screen mirroring (nametable 1)
--10b - Vert. mirroring
--11b - Horiz. mirroring
CHRMirr_s <= MMCReg0_s(1 downto 0);
CIRAMA10_p <= '0' when CHRMirr_s = "00" else
'1' when CHRMirr_s = "01" else
PPUA10_p when CHRMirr_s = "10" else
PPUA11_p when CHRMirr_s = "11" else
'0';

--**************************************************************
--CHR ROM banking
CHRA10_p <= PPUA10_p;
CHRA11_p <= PPUA11_p;
CHRA12_p <= ChrAddr16to12_s(0);
CHRA13_p <= ChrAddr16to12_s(1);
CHRA14_p <= ChrAddr16to12_s(2);
CHRA15_p <= ChrAddr16to12_s(3);
CHRA16_p <= ChrAddr16to12_s(4);
CHRA17_p <= '0';
CHRBanking : process (PPUA13_p, PPUA12_p)
begin
--check bank size
if (MMCReg0_s(4) = '0') then
--0 - Single 8K bank in CHR space.
--8K bank mode, this selects a full 8K bank at 0000h on the PPU space.
ChrAddr16to12_s <= MMCReg1_s(4 downto 1) & PPUA12_p;
else
--1 - Two 4K banks in CHR space.
--4K bank mode, this selects a 4K bank at 0000h on the PPU space.
if (PPUA12_p = '0') then
ChrAddr16to12_s <= MMCReg1_s(4 downto 0);
else
ChrAddr16to12_s <= MMCReg2_s(4 downto 0);
end if;
end if;
end process;

--**************************************************************
--PRG ROM banking
nPRGCE_p <= nROMSEL_p;
PRGA13_p <= CPUA13_p;
PRGA14_p <= RomAddr17to14_s(0);
PRGA15_p <= RomAddr17to14_s(1);
PRGA16_p <= RomAddr17to14_s(2);
PRGA17_p <= RomAddr17to14_s(3);
PRGA18_p <= '0';
PRGBanking : process (nROMSEL_p, CPUA14_p)
begin
--check bank size
if (MMCReg0_s(3) = '1') then
--16K mode, this selects a 16K bank in either 8000-BFFFh
--or C000-FFFFh depending on the state of the "H" bit in register 0.
--check which bank is swappable
if (MMCReg0_s(2) = '1') then
--1 - Bank C000-FFFFh is fixed, while 8000-FFFFh is swappable. (power-on default)
--fix last bank at $C000 and switch 16 KB bank at $8000
if (CPUA14_p = '0') then --first bank
RomAddr17to14_s <= MMCReg3_s(3 downto 0);
else --last bank
RomAddr17to14_s <= "1111";
end if;
else
--0 - Bank 8000-BFFFh is fixed, while C000-FFFFh is swappable
--fix first bank at $8000 and switch 16 KB bank at $C000;
if (CPUA14_p = '1') then --last bank
RomAddr17to14_s <= MMCReg3_s(3 downto 0);
else --first bank
RomAddr17to14_s <= "0000";
end if;
end if;
else
--32K mode, this selects a full 32K bank in the PRG space.
--Only the upper 3 bits are used then.
RomAddr17to14_s(3 downto 0) <= MMCReg3_s(3 downto 1) & CPUA14_p;
end if;
end process;

--write to mapper registers state machine
--use A14 and A13 to determine the register being written to
--The first bit in is the LSB, while the last bit in is the MSB.
process (nROMSEL_p, CPURnW_p)
begin
if (falling_edge(CPURnW_p)) then
if (nROMSEL_p = '0') then
current_s <= next_s; --state change.
end if;
end if;
end process;

process (current_s, CPUDATA_p, nROMSEL_p)
begin
if (rising_edge(nROMSEL_p)) then
if (CPURnW_p = '0') then
case current_s is
when s0 =>
if (CPUDATA_p(7) = '1') then
next_s <= s0;
case cpuAddr15to13_s(1 downto 0) is
when "00" =>
MMCReg0_s <= "01100";
when "01" =>
MMCReg1_s <= "00000";
when "10" =>
MMCReg2_s <= "00000";
when "11" =>
MMCReg3_s <= "00000";
end case;
else
tempReg_s(3 downto 0) <= tempReg_s(4 downto 1);
tempReg_s(4) <= CPUDATA_p(0);
next_s <= s1;
end if;
when s1 =>
if (CPUDATA_p(7) = '1') then
next_s <= s0;
case cpuAddr15to13_s(1 downto 0) is
when "00" =>
MMCReg0_s <= "01100";
when "01" =>
MMCReg1_s <= "00000";
when "10" =>
MMCReg2_s <= "00000";
when "11" =>
MMCReg3_s <= "00000";
end case;
else
tempReg_s(3 downto 0) <= tempReg_s(4 downto 1);
tempReg_s(4) <= CPUDATA_p(0);
next_s <= s2;
end if;
when s2 =>
if (CPUDATA_p(7) = '1') then
next_s <= s0;
case cpuAddr15to13_s(1 downto 0) is
when "00" =>
MMCReg0_s <= "01100";
when "01" =>
MMCReg1_s <= "00000";
when "10" =>
MMCReg2_s <= "00000";
when "11" =>
MMCReg3_s <= "00000";
end case;
else
tempReg_s(3 downto 0) <= tempReg_s(4 downto 1);
tempReg_s(4) <= CPUDATA_p(0);
next_s <= s3;
end if;
when s3 =>
if (CPUDATA_p(7) = '1') then
next_s <= s0;
case cpuAddr15to13_s(1 downto 0) is
when "00" =>
MMCReg0_s <= "01100";
when "01" =>
MMCReg1_s <= "00000";
when "10" =>
MMCReg2_s <= "00000";
when "11" =>
MMCReg3_s <= "00000";
end case;
else
tempReg_s(3 downto 0) <= tempReg_s(4 downto 1);
tempReg_s(4) <= CPUDATA_p(0);
next_s <= s4;
end if;
when s4 =>
if (CPUDATA_p(7) = '1') then
next_s <= s0;
case cpuAddr15to13_s(1 downto 0) is
when "00" =>
MMCReg0_s <= "01100";
when "01" =>
MMCReg1_s <= "00000";
when "10" =>
MMCReg2_s <= "00000";
when "11" =>
MMCReg3_s <= "00000";
end case;
else
case cpuAddr15to13_s(1 downto 0) is
when "00" =>
MMCReg0_s(3 downto 0) <= tempReg_s(4 downto 1);
MMCReg0_s(4) <= CPUDATA_p(0);
when "01" =>
MMCReg1_s(3 downto 0) <= tempReg_s(4 downto 1);
MMCReg1_s(4) <= CPUDATA_p(0);
when "10" =>
MMCReg2_s(3 downto 0) <= tempReg_s(4 downto 1);
MMCReg2_s(4) <= CPUDATA_p(0);
when "11" =>
MMCReg3_s(3 downto 0) <= tempReg_s(4 downto 1);
MMCReg3_s(4) <= CPUDATA_p(0);
end case;
next_s <= s0;
end if;
when others =>
next_s <= s0;
end case;
end if;
end if;
end process;


end MMC1_a;



=====================================================
to 70 MACROCELLS
decrease it
here using 40 macrocells
from a latin friend who is on my facebook fine people
David
=================> --NES MMC1 mapper implementation.
--Copyright 2013 David Senabre Albujer
--Free-open source implementation.
--
--Tested succesfully in on Xilinx XC9572 CPLD and SNROM PCB
--
--website:
--www.consolasparasiempre.net
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

--MMC1 Pinout:
-- .---\/---.
--PRG A14 (r) - |01 24| - +5V
--PRG A15 (r) - |02 23| - M2
--PRG A16 (r) - |03 22| - PRG A13 (s)
--PRG A17 (r) - |04 21| - PRG A14 (n)
--PRG /CE (r) - |05 20| - PRG /CE (n)
--WRAM CE (w) - |06 19| - PRG D7 (s)
--CHR A12 (r) - |07 18| - PRG D0 (s)
--CHR A13 (r) - |08 17| - PRG R/W
--CHR A14 (r) - |09 16| - CIRAM A10 (n)
--CHR A15 (r) - |10 15| - CHR A12 (n)
--CHR A16 (r) - |11 14| - CHR A11 (s)
-- GND - |12 13| - CHR A10 (s)
-- `--------'

-- Kid Icarus and Metroid are a SNROM cartirdge.
-- SNROM PCB uses most significant bit of chr_a_out to enable WRAM.

entity mmc1 is
Port ( pgr_d : in STD_LOGIC_VECTOR (1 downto 0);
pgr_ce_in : in STD_LOGIC; -- also called /ROMSEL
pgr_rw : in STD_LOGIC;
m2 : in STD_LOGIC;
pgr_a_in : in STD_LOGIC_VECTOR (1 downto 0);
chr_a_in : in STD_LOGIC_VECTOR (2 downto 0);
ciram : out STD_LOGIC;
pgr_a_out : out STD_LOGIC_VECTOR (3 downto 0);
chr_a_out : out STD_LOGIC_VECTOR (4 downto 0);
pgr_ce_out: out STD_LOGIC;
wram_ce : out STD_LOGIC
-- DEBUG SIGNALS
-- clk_dbg : out std_logic;
-- data_dbg : out STD_LOGIC_VECTOR(3 downto 0);
-- reg0_dbg : out STD_LOGIC_VECTOR(4 downto 0)
);
end mmc1;

architecture arch of mmc1 is

signal regs_e: STD_LOGIC;
signal reg0_e: STD_LOGIC;
signal reg1_e: STD_LOGIC;
signal reg2_e: STD_LOGIC;
signal reg3_e: STD_LOGIC;

signal rClk : STD_LOGIC;
signal shOut: STD_LOGIC_VECTOR(3 downto 0);

signal rPIn: STD_LOGIC_VECTOR(4 downto 0);
signal r0Out: STD_LOGIC_VECTOR(4 downto 0);
signal r1Out: STD_LOGIC_VECTOR(4 downto 0);
signal r2Out: STD_LOGIC_VECTOR(4 downto 0);
signal r3Out: STD_LOGIC_VECTOR(4 downto 0);

-- NOTE.
-- register 0 ($8000-$9FFF) must start with bits set, rather tan reset, why?
-- Because bit 2 must be set, for last ROM slot ($C000-$FFFF) to be fixed to
-- last PGR ROM bank, required for games with interrupt vectors in the last
-- bank to boot.
-- The rest of register 0 bits can be initially set without harm.

-- This behaviour is implemented by a generic INIT in std_reg.
-- In Xilinx enviroment, you can also set the start-up state using attribute

--attribute INIT : string;
--attribute INIT of shOut : signal is "S";

-- or use, in ucf file:

-- INST r0 INIT=S;

begin

-- control (state machine)
fsm: entity work.fsm(arch)
port map( sRst=>"not"(pgr_d(1)), -- bit D7 resets sequence
clk=>rClk,
reg_e=>regs_e );

-- shift register
sh0 : entity work.shift_reg(arch)
generic map( 4 )
port map( sIn=>pgr_d(0),
clk=>rClk,
rst=>"not"( pgr_d(1) ),
pOut=>shOut);
-- control reg
r0 : entity work.std_reg(arch)
generic map( 5, '1' )
port map( pIn=>rPIn,
clk=>rClk,
en=>reg0_e,
rst=>'1',
pOut=>r0Out);
-- chr rom 1
r1 : entity work.std_reg(arch)
generic map( 5 )
port map( pIn=>rPIn,
clk=>rClk,
en=>reg1_e,
rst=>'1',
pOut=>r1Out);
-- chr rom 2
r2 : entity work.std_reg(arch)
generic map( 5 )
port map( pIn=>rPIn,
clk=>rClk,
en=>reg2_e,
rst=>'1',
pOut=>r2Out);
-- pgr rom
r3 : entity work.std_reg(arch)
generic map( 5 )
port map( pIn=>rPIn,
clk=>rClk,
en=>reg3_e,
rst=>'1',
pOut=>r3Out);

-- registers input
rPIn <= pgr_d(0) & shOut;

-- internal clock
-- when writing, ROMSEL asserted and M2 is high (bus is stable)
rClk <= '1' when (m2='0' and pgr_ce_in='0' and pgr_rw='0') else
'0';
-- Be careful: the following does NOT work, unless m2 is internally delayed
-- by at least 100ns. Althoug this can be easily be accomplished, this is a
-- device-dependent solution, and not an elegant approach.
--rClk <= '1' when (m2='1' and pgr_ce_in='0' and pgr_rw='0') else
-- '0';

-- mmc1 register address decoding
reg0_e <= '0' when (pgr_a_in = "00" and regs_e = '1' ) else
'1';
reg1_e <= '0' when (pgr_a_in = "01" and regs_e = '1' ) else
'1';
reg2_e <= '0' when (pgr_a_in = "10" and regs_e = '1' ) else
'1';
reg3_e <= '0' when (pgr_a_in = "11" and regs_e = '1' ) else
'1';

-- PGR ROM bank selection
-- only 16 kb bank size is implemented
-- NOTE:
-- if reg 0: bit 2 = 0, $C000 is swappable.
-- if A14 = 1, address is inside $C000 bank, so, mapper provides selection
pgr_a_out <= r3Out(3 downto 0) when (r0Out(2) /= pgr_a_in(1)) else -- mapper provides bank selection
"0000" when (pgr_a_in(1) = '0') else -- access first slot (when fixed) 0x8000
"1111"; --when (pgr_a_in(1) = '1') -- access second slot (when fixed) 0xC000

-- CHR ROM bank selection
-- NOTE:
-- reg 0 bit 4 = 1, selects 4kb banks
-- chr_a_in(2) is PPU A12. Selects 4kb bank.
-- in 8kb mode, last bit of reg 2 is ignored, and replaced with current PPU A12.
chr_a_out <= r1Out when (r0Out(4) = '1' and chr_a_in(2)='0') else -- first 4Kb
r2Out when (r0Out(4) = '1' and chr_a_in(2)='1') else -- second 4Kb
r1Out(4 downto 1) & chr_a_in(2); -- 8Kb slot

-- mirroring
-- NOTE:
-- in vertical mirroring, PPU A10 is connected to CIRAM A10 (internal VRAM A10).
ciram <= chr_a_in(0) when r0Out(1 downto 0) = "10" else -- vertical mirroring
chr_a_in(1) when r0Out(1 downto 0) = "11" else -- horizontal mirroring
'0' when r0Out(1 downto 0) = "00" else -- no mirroring, nametable 0
'1';-- when r0Out(1 downto 0) = "01" else -- no mirroring, nametable 1

-- pgr /ce logic
pgr_ce_out <= pgr_ce_in when pgr_rw = '1' else
'1';

-- wram /ce logic
-- NOTE:
-- pgr_ce_in (/ROMSEL) is asserted when inside $8000-$FFFF, so it must be 1 below $8000
wram_ce <= '0' when (r3Out(4) = '1') else -- WRAM disabled?
'1' when (m2 = '1' and pgr_ce_in = '1' and pgr_a_in = "11") else -- enable WRAM 0x6000-0x7FFF (a13-14 = "11")
'0';
-- always enabled WRAM MMC1 version
-- wram_ce <= '1' when (m2 = '1' and pgr_ce_in = '1' and pgr_a_in = "11") else -- enable WRAM 0x6000-0x7FFF (a13-14 = "11")
-- '0';


-- DEBUG SIGNALS
--clk_dbg <= rClk;
--data_dbg <= shOut;
--reg0_dbg <= r3Out;

end arch;
Post Reply