// MMC3 style mappers. Some of these can probably be consolidated.

// iNES mapper 64 and 158 - Tengen's version of MMC3
module Rambo1(
	input        clk,         // System clock
	input        ce,          // M2 ~cpu_clk
	input        enable,      // Mapper enabled
	input [31:0] flags,       // Cart flags
	input [15:0] prg_ain,     // prg address
	inout [21:0] prg_aout_b,  // prg address out
	input        prg_read,    // prg read
	input        prg_write,   // prg write
	input  [7:0] prg_din,     // prg data in
	inout  [7:0] prg_dout_b,  // prg data out
	inout        prg_allow_b, // Enable access to memory for the specified operation.
	input [13:0] chr_ain,     // chr address in
	inout [21:0] chr_aout_b,  // chr address out
	input        chr_read,    // chr ram read
	inout        chr_allow_b, // chr allow write
	inout        vram_a10_b,  // Value for A10 address line
	inout        vram_ce_b,   // True if the address should be routed to the internal 2kB VRAM.
	inout        irq_b,       // IRQ
	input [15:0] audio_in,    // Inverted audio from APU
	inout [15:0] audio_b,     // Mixed audio output
	inout [15:0] flags_out_b, // flags {0, 0, 0, 0, has_savestate, prg_conflict, prg_bus_write, has_chr_dout}
	input [13:0] chr_ain_o,
	// savestates
	input       [63:0]  SaveStateBus_Din,
	input       [ 9:0]  SaveStateBus_Adr,
	input               SaveStateBus_wren,
	input               SaveStateBus_rst,
	input               SaveStateBus_load,
	output      [63:0]  SaveStateBus_Dout
);

assign prg_aout_b   = enable ? prg_aout : 22'hZ;
assign prg_dout_b   = enable ? prg_dout : 8'hZ;
assign prg_allow_b  = enable ? prg_allow : 1'hZ;
assign chr_aout_b   = enable ? chr_aout : 22'hZ;
assign chr_allow_b  = enable ? chr_allow : 1'hZ;
assign vram_a10_b   = enable ? vram_a10 : 1'hZ;
assign vram_ce_b    = enable ? vram_ce : 1'hZ;
assign irq_b        = enable ? irq : 1'hZ;
assign flags_out_b  = enable ? flags_out : 16'hZ;
assign audio_b      = enable ? {1'b0, audio_in[15:1]} : 16'hZ;

wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout = 0;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg irq;
reg [15:0] flags_out = {12'h0, 1'b1, 3'b0};

reg [3:0] bank_select;             // Register to write to next
reg prg_rom_bank_mode;             // Mode for PRG banking
reg chr_K;                         // Mode for CHR banking
reg chr_a12_invert;                // Mode for CHR banking
reg mirroring;                     // 0 = vertical, 1 = horizontal
reg irq_enable, irq_reload;        // IRQ enabled, and IRQ reload requested
reg [7:0] irq_latch, counter;      // IRQ latch value and current counter
reg [1:0] irq_delay;
reg [7:0] chr_bank_0, chr_bank_1;  // Selected CHR banks
reg [7:0] chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5;
reg [7:0] chr_bank_8, chr_bank_9;
reg [5:0] prg_bank_0, prg_bank_1, prg_bank_2;  // Selected PRG banks
reg irq_cycle_mode, next_irq_cycle_mode;
reg [1:0] cycle_counter;

// Mapper has vram_a10 wired to CHR A17
//wire mapper64 = (flags[7:0] == 64);//default
wire mapper158 = (flags[7:0] == 158);

// This code detects rising edges on a12.
reg old_a12_edge, a12_edge_delayed;
reg [4:0] a12_ctr;
wire a12_edge = (chr_ain_o[12] && a12_ctr == 0) || old_a12_edge;

always @(posedge clk) begin
	if (SaveStateBus_load) begin
		old_a12_edge       <= SS_MAP1[   33];
		a12_ctr            <= SS_MAP1[38:34];
	end else begin
		old_a12_edge <= a12_edge && !ce;
		if (ce) begin
			if (chr_ain_o[12]) begin
				a12_ctr <= 5'd16;
			end else if (a12_ctr > 0) begin
				a12_ctr <= a12_ctr - 1'd1;
			end
		end
	end
end

always @(posedge clk)
if (~enable) begin
	bank_select <= 0;
	prg_rom_bank_mode <= 0;
	chr_K <= 0;
	chr_a12_invert <= 0;
	mirroring <= 0;
	{irq_enable, irq_reload} <= 0;
	{irq_latch, counter} <= 0;
	{chr_bank_0, chr_bank_1} <= 0;
	{chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5} <= 0;
	{chr_bank_8, chr_bank_9} <= 0;
	{prg_bank_0, prg_bank_1, prg_bank_2} <= 6'b111111;
	irq_cycle_mode <= 0;
	next_irq_cycle_mode <= 0;
	cycle_counter <= 0;
	irq <= 0;
	irq_delay <= 0;
	a12_edge_delayed <= 0;
end else if (SaveStateBus_load) begin
	irq                <= SS_MAP1[    0];
	cycle_counter      <= SS_MAP1[ 2: 1];
	irq_cycle_mode     <= SS_MAP1[    3];
	next_irq_cycle_mode<= SS_MAP1[    4];
	a12_edge_delayed   <= SS_MAP1[    5];
	bank_select        <= SS_MAP1[ 9: 6];
	prg_rom_bank_mode  <= SS_MAP1[   10];
	chr_a12_invert     <= SS_MAP1[   11];
	mirroring          <= SS_MAP1[   12];
	irq_enable         <= SS_MAP1[   13];
	irq_reload         <= SS_MAP1[   14];
	irq_latch          <= SS_MAP1[22:15];
	counter            <= SS_MAP1[30:23];
	irq_delay          <= SS_MAP1[32:31];
//	old_a12_edge       <= SS_MAP1[   33];
//	a12_ctr            <= SS_MAP1[38:34];
	chr_bank_0         <= SS_MAP1[46:39];
	chr_bank_1         <= SS_MAP1[54:47];
	chr_bank_2         <= SS_MAP1[62:55];
	chr_K              <= SS_MAP1[   63];
	chr_bank_3         <= SS_MAP2[ 7: 0];
	chr_bank_4         <= SS_MAP2[15: 8];
	chr_bank_5         <= SS_MAP2[23:16];
	prg_bank_0         <= SS_MAP2[29:24];
	prg_bank_1         <= SS_MAP2[35:30];
	prg_bank_2         <= SS_MAP2[41:36];
	chr_bank_8         <= SS_MAP2[49:42];
	chr_bank_9         <= SS_MAP2[57:50];
end else if (ce) begin
	// Process these before writes so irq_reload and cycle_counter register writes take precedence.
	cycle_counter <= cycle_counter + 1'd1;
	irq_cycle_mode <= next_irq_cycle_mode;
	a12_edge_delayed <= a12_edge;

	if (irq_cycle_mode ? (cycle_counter == 3) : a12_edge_delayed) begin
		if (irq_reload) begin
		    if (|irq_latch) begin
				// Wiki and Nintendulator source: OR with 1 if not zero
				counter <= irq_latch | 8'h01;
			end else begin
				counter <= 8'h00;
			end
			if (~|irq_latch && irq_enable) begin
				irq_delay <= 1;
			end
			irq_reload <= 0;
		end
		else if (counter == 8'h00) begin
			counter <= irq_latch;
			if (~|irq_latch && irq_enable) begin
				irq_delay <= 1;
			end
		end
		else begin
			counter <= counter - 1'd1;
			if (counter == 8'h01 && irq_enable) begin
				irq_delay <= 1;
			end
		end
	end

	if (irq_delay) begin
		irq <= 1;
		irq_delay <= 0;
	end

	if (prg_write && prg_ain[15]) begin
		case({prg_ain[14:13], prg_ain[0]})
			// Bank select ($8000-$9FFE, even)
			3'b00_0: {chr_a12_invert, prg_rom_bank_mode, chr_K, bank_select} <= {prg_din[7:5], prg_din[3:0]};
			// Bank data ($8001-$9FFF, odd)
			3'b00_1:
				case (bank_select)
					0: chr_bank_0 <= prg_din;       // Select 2 (K=0) or 1 (K=1) KB CHR bank at PPU $0000 (or $1000);
					1: chr_bank_1 <= prg_din;       // Select 2 (K=0) or 1 (K=1) KB CHR bank at PPU $0800 (or $1800);
					2: chr_bank_2 <= prg_din;       // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
					3: chr_bank_3 <= prg_din;       // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
					4: chr_bank_4 <= prg_din;       // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
					5: chr_bank_5 <= prg_din;       // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
					6: prg_bank_0 <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
					7: prg_bank_1 <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $A000-$BFFF
					8: chr_bank_8 <= prg_din;       // If K=1, Select 1 KB CHR bank at PPU $0400 (or $1400);

					15: prg_bank_2 <= prg_din[5:0]; // Select 8 KB PRG ROM bank at $C000-$DFFF (or $8000-$9FFF);
				endcase
			3'b01_0: mirroring <= prg_din[0];                   // Mirroring ($A000-$BFFE, even)
			3'b01_1: begin end
			3'b10_0: irq_latch <= prg_din;                      // IRQ latch ($C000-$DFFE, even)
			3'b10_1: begin
						{irq_reload, next_irq_cycle_mode} <= {1'b1, prg_din[0]}; // IRQ reload ($C001-$DFFF, odd)
						cycle_counter <= 0;
					end
			3'b11_0: {irq_enable, irq} <= 2'b00;                 // IRQ disable ($E000-$FFFE, even)
			3'b11_1: {irq_enable, irq} <= 2'b10;                 // IRQ enable ($E001-$FFFF, odd)
		endcase
	end
end

assign SS_MAP1_BACK[    0] = irq;
assign SS_MAP1_BACK[ 2: 1] = cycle_counter;
assign SS_MAP1_BACK[    3] = irq_cycle_mode;
assign SS_MAP1_BACK[    4] = next_irq_cycle_mode;
assign SS_MAP1_BACK[    5] = a12_edge_delayed;
assign SS_MAP1_BACK[ 9: 6] = bank_select;
assign SS_MAP1_BACK[   10] = prg_rom_bank_mode;
assign SS_MAP1_BACK[   11] = chr_a12_invert;
assign SS_MAP1_BACK[   12] = mirroring;
assign SS_MAP1_BACK[   13] = irq_enable;
assign SS_MAP1_BACK[   14] = irq_reload;
assign SS_MAP1_BACK[22:15] = irq_latch;
assign SS_MAP1_BACK[30:23] = counter;
assign SS_MAP1_BACK[32:31] = irq_delay;
assign SS_MAP1_BACK[   33] = old_a12_edge;
assign SS_MAP1_BACK[38:34] = a12_ctr;
assign SS_MAP1_BACK[46:39] = chr_bank_0;
assign SS_MAP1_BACK[54:47] = chr_bank_1;
assign SS_MAP1_BACK[62:55] = chr_bank_2;
assign SS_MAP1_BACK[   63] = chr_K;

assign SS_MAP2_BACK[ 7: 0] = chr_bank_3;
assign SS_MAP2_BACK[15: 8] = chr_bank_4;
assign SS_MAP2_BACK[23:16] = chr_bank_5;
assign SS_MAP2_BACK[29:24] = prg_bank_0;
assign SS_MAP2_BACK[35:30] = prg_bank_1;
assign SS_MAP2_BACK[41:36] = prg_bank_2;
assign SS_MAP2_BACK[49:42] = chr_bank_8;
assign SS_MAP2_BACK[57:50] = chr_bank_9;
assign SS_MAP2_BACK[63:58] = 6'b0; // free to be used

// The PRG bank to load. Each increment here is 8kb. So valid values are 0..63.
reg [5:0] prgsel;
always @* begin
	casez({prg_ain[14:13], prg_rom_bank_mode})
		3'b00_0: prgsel = prg_bank_0;  // $8000 is R:6
		3'b01_0: prgsel = prg_bank_1;  // $A000 is R:7
		3'b10_0: prgsel = prg_bank_2;  // $C000 is R:F
		3'b11_0: prgsel = 6'b111111;   // $E000 fixed to last bank
		3'b00_1: prgsel = prg_bank_2;  // $8000 is R:F
		3'b01_1: prgsel = prg_bank_0;  // $A000 is R:6
		3'b10_1: prgsel = prg_bank_1;  // $C000 is R:7
		3'b11_1: prgsel = 6'b111111;   // $E000 fixed to last bank
	endcase
end

// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
reg [7:0] chrsel;

always @* begin
	casez({chr_ain[12] ^ chr_a12_invert, chr_ain[11], chr_ain[10], chr_K})
		4'b00?_0: chrsel = {chr_bank_0[7:1], chr_ain[10]};
		4'b01?_0: chrsel = {chr_bank_1[7:1], chr_ain[10]};
		4'b000_1: chrsel = chr_bank_0;
		4'b001_1: chrsel = chr_bank_8;
		4'b010_1: chrsel = chr_bank_1;
		4'b011_1: chrsel = chr_bank_9;
		4'b100_?: chrsel = chr_bank_2;
		4'b101_?: chrsel = chr_bank_3;
		4'b110_?: chrsel = chr_bank_4;
		4'b111_?: chrsel = chr_bank_5;
	endcase
end

assign prg_aout = {3'b00_0,  prgsel, prg_ain[12:0]};
assign {chr_allow, chr_aout} = {flags[15], 4'b10_00, chrsel, chr_ain[9:0]};
assign prg_allow = prg_ain[15] && !prg_write;
assign vram_a10 = mapper158 ? chrsel[7] :  // Mapper 158 controls mirroring by switching the top bits of the CHR address
		mirroring ? chr_ain[11] : chr_ain[10];
assign vram_ce = chr_ain[13];

// savestate
localparam SAVESTATE_MODULES    = 2;
wire [63:0] SaveStateBus_wired_or[0:SAVESTATE_MODULES-1];
wire [63:0] SS_MAP1, SS_MAP2;
wire [63:0] SS_MAP1_BACK, SS_MAP2_BACK;
wire [63:0] SaveStateBus_Dout_active = SaveStateBus_wired_or[0] | SaveStateBus_wired_or[1];

eReg_SavestateV #(SSREG_INDEX_MAP1, 64'h0000000000000000) iREG_SAVESTATE_MAP1 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[0], SS_MAP1_BACK, SS_MAP1);
eReg_SavestateV #(SSREG_INDEX_MAP2, 64'h0000000000000000) iREG_SAVESTATE_MAP2 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[1], SS_MAP2_BACK, SS_MAP2);

assign SaveStateBus_Dout = enable ? SaveStateBus_Dout_active : 64'h0000000000000000;

endmodule

// This mapper also handles mapper 33,47,48,74,76,80,82,88,95,118,119,154,191,192,194,195,206 and 207.
// The line commented with Warning 10027 throws a synthesis warning that is suppressed
// altera message_off 10027
module MMC3 (
	input        clk,         // System clock
	input        ce,          // M2 ~cpu_clk
	input        enable,      // Mapper enabled
	input [31:0] flags,       // Cart flags
	input [15:0] prg_ain,     // prg address
	inout [21:0] prg_aout_b,  // prg address out
	input        prg_read,    // prg read
	input        prg_write,   // prg write
	input  [7:0] prg_din,     // prg data in
	inout  [7:0] prg_dout_b,  // prg data out
	inout        prg_allow_b, // Enable access to memory for the specified operation.
	input [13:0] chr_ain,     // chr address in
	inout [21:0] chr_aout_b,  // chr address out
	input        chr_read,    // chr ram read
	inout        chr_allow_b, // chr allow write
	inout        vram_a10_b,  // Value for A10 address line
	inout        vram_ce_b,   // True if the address should be routed to the internal 2kB VRAM.
	inout        irq_b,       // IRQ
	input [15:0] audio_in,    // Inverted audio from APU
	inout [15:0] audio_b,     // Mixed audio output
	inout [15:0] flags_out_b, // flags {0, 0, 0, 0, has_savestate, prg_conflict, prg_bus_write, has_chr_dout}
	input [13:0] chr_ain_o,
	input        m2_inv,
	input        paused,
	// savestates
	input       [63:0]  SaveStateBus_Din,
	input       [ 9:0]  SaveStateBus_Adr,
	input               SaveStateBus_wren,
	input               SaveStateBus_rst,
	input               SaveStateBus_load,
	output      [63:0]  SaveStateBus_Dout
);

assign prg_aout_b   = enable ? prg_aout : 22'hZ;
assign prg_dout_b   = enable ? prg_dout : 8'hZ;
assign prg_allow_b  = enable ? prg_allow : 1'hZ;
assign chr_aout_b   = enable ? chr_aout : 22'hZ;
assign chr_allow_b  = enable ? chr_allow : 1'hZ;
assign vram_a10_b   = enable ? vram_a10 : 1'hZ;
assign vram_ce_b    = enable ? vram_ce : 1'hZ;
assign irq_b        = enable ? irq : 1'hZ;
assign flags_out_b  = enable ? flags_out : 16'hZ;
assign audio_b      = enable ? {1'b0, audio_in[15:1]} : 16'hZ;

wire [21:0] prg_aout, chr_aout;
reg [7:0] prg_dout;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire irq;
wire [15:0] flags_out = {12'h0, 1'b1, 1'b0, prg_bus_write, 1'b0};
reg prg_bus_write;

reg [2:0] bank_select;             // Register to write to next
reg prg_rom_bank_mode;             // Mode for PRG banking
reg chr_a12_invert;                // Mode for CHR banking
reg mirroring;                     // 0 = vertical, 1 = horizontal
reg irq_enable, irq_reload;        // IRQ enabled, and IRQ reload requested
reg [7:0] irq_latch, counter;      // IRQ latch value and current counter
reg [3:0] ram_enable, ram_protect;       // RAM protection bits
reg ram6_enabled, ram6_enable, ram6_protect; //extra bits for mmc6
reg [7:0] chr_bank_0, chr_bank_1;  // Selected CHR banks
reg chr_bank_0_0, chr_bank_1_0;    // Keep extra bit
reg [7:0] chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5;
reg [7:0] prg_bank_0, prg_bank_1, prg_bank_2;  // Selected PRG banks
reg last_a12;
wire prg_is_ram;
reg [6:0] irq_reg;
assign irq = mapper48 ? irq_reg[3] & irq_enable : irq_reg[0];
reg [7:0] m268_reg [5:0];

// The alternative behavior has slightly different IRQ counter semantics.
wire mmc3_alt_behavior = acclaim;

wire TQROM =     (flags[7:0] == 119); 	// TQROM maps 8kB CHR RAM
wire TxSROM =    (flags[7:0] == 118); 	// Connects CHR A17 to CIRAM A10
wire mapper47 =  (flags[7:0] == 47);		// Mapper 47 is a multicart that has 128k for each game. It has no RAM.
wire mapper37 =  (flags[7:0] == 37);    // European Triple Cart (Super Mario, Tetris, Nintendo World Cup)
wire DxROM =     (flags[7:0] == 206);
wire mapper112 = (flags[7:0] == 112);   // Ntdec
wire mapper48 =  (flags[7:0] == 48);    // Taito's TC0690
wire mapper33 =  (flags[7:0] == 33);    // Taito's TC0190 (TC0690-like. No IRQ. Different Mirroring bit)
wire mapper95 =  (flags[7:0] == 95);    // NAMCOT-3425
wire mapper88 =  (flags[7:0] == 88);    // NAMCOT-3433
wire mapper154 = (flags[7:0] == 154);   // NAMCOT-3453
wire mapper76 =  (flags[7:0] == 76);    // NAMCOT-3446
wire mapper80 =  (flags[7:0] == 80);    // Taito's X1-005
wire mapper82 =  (flags[7:0] == 82);    // Taito's X1-017
wire mapper207 = (flags[7:0] == 207);   // Taito's X1-017
wire mapper74 =  (flags[7:0] == 74);    // Has 2KB CHR RAM
wire mapper191 = (flags[7:0] == 191);   // Has 2KB CHR RAM
wire mapper192 = (flags[7:0] == 192);   // Has 4KB CHR RAM
wire mapper194 = (flags[7:0] == 194);   // Has 2KB CHR RAM
wire mapper195 = (flags[7:0] == 195);   // Has 4KB CHR RAM
wire mapper196 = (flags[7:0] == 196);   // PRG A0 line switcheroo
wire mapper189 = (flags[7:0] == 189);		// Mapper 189 is a multicart
wire mapper205 = (flags[7:0] == 205);		// Mapper 47 is a multicart similar to mapper 37
wire mapper208 = (flags[7:0] == 208);		// Mapper 208 is a multicart similar to mapper 189 with copy protection. It has no RAM.
wire MMC6 = ((flags[7:0] == 4) && (flags[24:21] == 1)); // mapper 4, submapper 1 = MMC6
wire acclaim = ((flags[7:0] == 4) && (flags[24:21] == 3)); // Acclaim mapper
wire mapper268 = ({flags[20:17],flags[7:0]} == 268); // Coolboy/Mindkids; Note: if mapper 268-256=12 was in this driver, it would need to check upper mapper bits
wire mapper268_5k = (flags[24:21] == 1);
wire oversized = mapper268 || (flags[10:9] == 3); // If prg size in header is >= 1MB (prg_size==6 or 7) must be some way to access it. Allow oversize mmc3
wire gnrom;
wire lockout;
wire gnrom_lock;
wire mega_unrom;
wire weird_mode;

wire four_screen_mirroring = flags[16];// | DxROM; // not all DxROM are 4-screen
reg mapper47_multicart;
reg [2:0] mapper37_multicart;
reg [3:0] mapper189_prgsel;
wire [7:0] new_counter = (counter == 0 || irq_reload) ? irq_latch : counter - 1'd1;
reg [3:0] a12_ctr;
wire irq_support = !DxROM && !mapper33 && !mapper95 && !mapper88 && !mapper154 && !mapper76
	&& !mapper80 && !mapper82 && !mapper207 && !mapper112; //82,207 not needed
wire prg_invert_support = (irq_support && !mapper48);
wire chr_invert_support = (irq_support && !mapper48) || mapper82;
wire regs_7e = mapper80 || mapper82 || mapper207;
wire internal_128 = mapper80 || mapper207;
wire prg_reg_odd = (~mapper196) ? prg_ain[0] : ( |prg_ain[3:2] | (prg_ain[1] & ~prg_ain[14]) );
wire [3:0] prota = (m268_reg[4][6] ^ m268_reg[4][3]) ? {m268_reg[4][1:0],m268_reg[4][4],m268_reg[4][7]} : 4'hF; // m208 4'hF = 0x59
wire [3:0] prot = (m268_reg[4][3]) ? ~prota : prota;

always @(posedge clk)
if (~enable) begin
	irq_reg <= 7'b0000000;
	bank_select <= 0;
	prg_rom_bank_mode <= 0;
	chr_a12_invert <= 0;
	mirroring <= flags[14];
	{irq_enable, irq_reload} <= 0;
	{irq_latch, counter} <= 0;
	ram_enable <= {4{mapper112}};
	ram_protect <= 0;
	{chr_bank_0, chr_bank_1} <= 0;
	{chr_bank_2, chr_bank_3, chr_bank_4, chr_bank_5} <= 0;
	{prg_bank_0, prg_bank_1} <= 0;
	prg_bank_2 <= 8'b11111110;
	a12_ctr <= 0;
	last_a12 <= 0;
	mapper37_multicart <= 3'b000;
	mapper189_prgsel <= 4'b1011; // mapper 208 requires 0xX011
	{m268_reg[0],m268_reg[1],m268_reg[2],m268_reg[3],m268_reg[4],m268_reg[5]} <= 0;
end else if (SaveStateBus_load) begin
	irq_reg            <= SS_MAP1[ 6: 0];
	bank_select        <= SS_MAP1[ 9: 7];
	prg_rom_bank_mode  <= SS_MAP1[   10];
	chr_a12_invert     <= SS_MAP1[   11];
	mirroring          <= SS_MAP1[   12];
	irq_enable         <= SS_MAP1[   13];
	irq_reload         <= SS_MAP1[   14];
	irq_latch          <= SS_MAP1[22:15];
	counter            <= SS_MAP1[30:23];
	ram_enable         <= SS_MAP1[34:31];
	ram_protect        <= SS_MAP1[38:35];
	chr_bank_0         <= SS_MAP1[46:39];
	chr_bank_1         <= SS_MAP1[54:47];
	chr_bank_2         <= SS_MAP1[62:55];
	chr_bank_3         <= SS_MAP2[ 7: 0];
	chr_bank_4         <= SS_MAP2[15: 8];
	chr_bank_5         <= SS_MAP2[23:16];
	prg_bank_0[5:0]    <= SS_MAP2[29:24];
	prg_bank_1[5:0]    <= SS_MAP2[35:30];
	prg_bank_2[5:0]    <= SS_MAP2[41:36];
	a12_ctr            <= SS_MAP2[45:42];
	last_a12           <= SS_MAP2[   46];
	mapper37_multicart <= SS_MAP2[49:47];
	mapper47_multicart <= SS_MAP2[   50];
	mapper189_prgsel   <= SS_MAP2[54:51];
	ram6_enabled       <= SS_MAP2[   55];
	ram6_enable        <= SS_MAP2[   56];
	ram6_protect       <= SS_MAP2[   57];
	prg_bank_0[7:6]    <= SS_MAP2[59:58];
	prg_bank_1[7:6]    <= SS_MAP2[61:60];
	prg_bank_2[7:6]    <= SS_MAP2[63:62];
	m268_reg[0]        <= SS_MAP3[ 7: 0];
	m268_reg[1]        <= SS_MAP3[15: 8];
	m268_reg[2]        <= SS_MAP3[23:16];
	m268_reg[3]        <= SS_MAP3[31:24];
	m268_reg[4]        <= SS_MAP3[39:32];
	m268_reg[5]        <= SS_MAP3[47:40];
	chr_bank_0_0       <= SS_MAP1[   48];
	chr_bank_1_0       <= SS_MAP1[   49];
end else begin
	if (ce) begin // M2
		if (!regs_7e && prg_write && prg_ain[15]) begin
			if (!mapper33 && !mapper48 && !mapper112) begin
				casez({prg_ain[14:13], prg_reg_odd})
					3'b00_0: {chr_a12_invert, prg_rom_bank_mode, ram6_enabled, bank_select} <= {prg_din[7:5], prg_din[2:0]}; // Bank select ($8000-$9FFE, even)
					3'b00_1: begin // Bank data ($8001-$9FFF, odd)
						case (bank_select)
							0: {chr_bank_0,chr_bank_0_0} <= {1'b0,prg_din};  // Select 2 KB CHR bank at PPU $0000-$07FF (or $1000-$17FF);
							1: {chr_bank_1,chr_bank_1_0} <= {1'b0,prg_din};  // Select 2 KB CHR bank at PPU $0800-$0FFF (or $1800-$1FFF);
							2: chr_bank_2 <= prg_din;       // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
							3: chr_bank_3 <= prg_din;       // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
							4: chr_bank_4 <= prg_din;       // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
							5: chr_bank_5 <= prg_din;       // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
							6: prg_bank_0 <= prg_din;       // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
							7: prg_bank_1 <= prg_din;       // Select 8 KB PRG ROM bank at $A000-$BFFF
						endcase
					end
					3'b01_0: if (!mapper208) mirroring <= !prg_din[0];  // Mirroring ($A000-$BFFE, even)
					3'b01_1: {ram_enable, ram_protect, ram6_enable, ram6_protect} <= {{4{prg_din[7]}},{4{prg_din[6]}}, prg_din[5:4]}; // PRG RAM protect ($A001-$BFFF, odd)
					3'b10_0: irq_latch <= prg_din;                      // IRQ latch ($C000-$DFFE, even)
					3'b10_1: irq_reload <= 1;                           // IRQ reload ($C001-$DFFF, odd)
					3'b11_0: {irq_enable, irq_reg[0]} <= 2'b00;         // IRQ disable ($E000-$FFFE, even)
					3'b11_1: irq_enable <= 1;                           // IRQ enable ($E001-$FFFF, odd)
				endcase
			end else if (!mapper112) begin
				casez({prg_ain[14:13], prg_ain[1:0], mapper48})
					5'b00_00_0: {mirroring, prg_bank_0[5:0]} <= prg_din[6:0] ^ 7'h40; // Select 8 KB PRG ROM bank at $8000-$9FFF
					5'b00_00_1: prg_bank_0[5:0] <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $8000-$9FFF
					5'b00_01_?: prg_bank_1[5:0] <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $A000-$BFFF
					5'b00_10_?: chr_bank_0 <= prg_din;  // Select 2 KB CHR bank at PPU $0000-$07FF
					5'b00_11_?: chr_bank_1 <= prg_din;  // Select 2 KB CHR bank at PPU $0800-$0FFF
					5'b01_00_?: chr_bank_2 <= prg_din;  // Select 1 KB CHR bank at PPU $1000-$13FF
					5'b01_01_?: chr_bank_3 <= prg_din;  // Select 1 KB CHR bank at PPU $1800-$1BFF
					5'b01_10_?: chr_bank_4 <= prg_din;  // Select 1 KB CHR bank at PPU $1800-$1BFF
					5'b01_11_?: chr_bank_5 <= prg_din;  // Select 1 KB CHR bank at PPU $1C00-$1FFF

					5'b10_00_1: irq_latch <= prg_din ^ 8'hFF;              // IRQ latch ($C000-$DFFC)
					5'b10_01_1: {irq_reload, irq_reg} <= 8'b10000000;      // IRQ reload ($C001-$DFFD)
					5'b10_10_1: irq_enable <= 1;                           // IRQ enable ($C002-$DFFE)
					5'b10_11_1: irq_enable <= 0;                           // IRQ disable ($C003-$DFFF)

					5'b11_00_1: mirroring <= !prg_din[6];  // Mirroring
				endcase
			end else begin
				casez({prg_ain[14:13], prg_ain[0]})
					3'b00_0: {bank_select} <= {prg_din[2:0]}; // Bank select ($8000-$9FFE)

					3'b01_0: begin // Bank data ($A000-$BFFF)
						case (bank_select)
						0: prg_bank_0 <= prg_din;       // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
						1: prg_bank_1 <= prg_din;       // Select 8 KB PRG ROM bank at $A000-$BFFF
						2: chr_bank_0 <= {1'b0,prg_din[7:1]};  // Select 2 KB CHR bank at PPU $0000-$07FF (or $1000-$17FF);
						3: chr_bank_1 <= {1'b0,prg_din[7:1]};  // Select 2 KB CHR bank at PPU $0800-$0FFF (or $1800-$1FFF);
						4: chr_bank_2 <= prg_din;       // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
						5: chr_bank_3 <= prg_din;       // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
						6: chr_bank_4 <= prg_din;       // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
						7: chr_bank_5 <= prg_din;       // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
						endcase
					end

					3'b11_0: mirroring <= !prg_din[0];  // Mirroring ($E000-$FFFE)
				endcase
			end

			if (mapper154)
				mirroring <= !prg_din[6];
			if (DxROM || mapper76 || mapper88)
				mirroring <= flags[14]; // Hard-wired mirroring
		end
		else if (regs_7e && prg_write && prg_ain[15:4]==12'h7EF) begin
			casez({prg_ain[3:0], mapper82})
				5'b0000_?: chr_bank_0 <= {1'b0,prg_din[7:1]};  // Select 2 KB CHR bank at PPU $0000-$07FF
				5'b0001_?: chr_bank_1 <= {1'b0,prg_din[7:1]};  // Select 2 KB CHR bank at PPU $0800-$0FFF
				5'b0010_?: chr_bank_2 <= prg_din;  // Select 1 KB CHR bank at PPU $1000-$13FF
				5'b0011_?: chr_bank_3 <= prg_din;  // Select 1 KB CHR bank at PPU $1800-$1BFF
				5'b0100_?: chr_bank_4 <= prg_din;  // Select 1 KB CHR bank at PPU $1800-$1BFF
				5'b0101_?: chr_bank_5 <= prg_din;  // Select 1 KB CHR bank at PPU $1C00-$1FFF
				5'b011?_0: {mirroring} <= prg_din[0]; // Select Mirroing
				5'b100?_0: {ram_enable[3], ram_protect[3]} <= {(prg_din==8'hA3),!(prg_din==8'hA3)};  // Enable RAM at $7F00-$7FFF
				5'b0110_1: {chr_a12_invert,mirroring} <= prg_din[1:0]; // Select Mirroing
				5'b0111_1: {ram_enable[0], ram_protect[0]} <= {(prg_din==8'hCA),!(prg_din==8'hCA)};  // Enable RAM at $6000-$67FF
				5'b1000_1: {ram_enable[1], ram_protect[1]} <= {(prg_din==8'h69),!(prg_din==8'h69)};  // Enable RAM at $6F00-$6FFF
				5'b1001_1: {ram_enable[2], ram_protect[2]} <= {(prg_din==8'h84),!(prg_din==8'h84)};  // Enable RAM at $7000-$73FF  //Using 6K; Require 5K instead?
				5'b101?_0: prg_bank_0[5:0] <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $8000-$9FFF
				5'b110?_0: prg_bank_1[5:0] <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $A000-$BFFF
				5'b111?_0: prg_bank_2[5:0] <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $C000-$DFFF
				5'b1010_1: prg_bank_0[5:0] <= prg_din[7:2];  // Select 8 KB PRG ROM bank at $8000-$9FFF
				5'b1011_1: prg_bank_1[5:0] <= prg_din[7:2];  // Select 8 KB PRG ROM bank at $A000-$BFFF
				5'b1100_1: prg_bank_2[5:0] <= prg_din[7:2];  // Select 8 KB PRG ROM bank at $C000-$DFFF
			endcase
		end

		if (mapper268 && prg_write && ({mapper268_5k,prg_ain[15:12]}==5'h6 || {mapper268_5k,prg_ain[15:12]}==5'h15)) begin
			if (prg_ain[2:0]==3'h2) begin
				m268_reg[2][3:0] <= prg_din[3:0];
				if (!gnrom_lock)
					m268_reg[2][7:4] <= prg_din[7:4];
			end else if ((prg_ain[2:1]!=2'b11) && !lockout) begin
				m268_reg[prg_ain[2:0]] <= prg_din;
			end
		end

		// For Mapper 47
		// $6000-7FFF:  [.... ...B]  Block select
		if (prg_write && prg_is_ram)
			mapper47_multicart <= prg_din[0];

		// For Mapper 37 and 205
		// $6000-7FFF:  [.... .QBB]  Block select
		if (prg_write && prg_is_ram)
			mapper37_multicart <= prg_din[2:0];

		// Mapper 189
		// $4120-7FFF:  [AAAA BBBB] A,B:  PRG Reg
		if (prg_write && prg_ain[15:14] == 2'b01 && prg_ain[8] && mapper189)
			mapper189_prgsel <= (prg_din[7:4] | prg_din[3:0]); // Select 32 KB PRG ROM bank at $8000-$FFFF

		// Mapper 208
		// $4800-4FFF or 6800-6FFF:  [..mP ...P] :  mirror PRG Reg
		if (prg_write && prg_ain[15:14] == 2'b01 && !prg_ain[12] && prg_ain[11] && mapper208)
			{mirroring, mapper189_prgsel[1:0]} <= {!prg_din[5], prg_din[4], prg_din[0]}; // Select 32 KB PRG ROM bank at $8000-$FFFF

		// Mapper 208
		// $5000-57FF: Prot index
		if (prg_write && prg_ain[15:11] == 5'b01010 && mapper208)
			m268_reg[4] <= prg_din;

		// Mapper 208
		// $5800-5FFF: Prot index
		if (prg_write && prg_ain[15:11] == 5'b01011 && mapper208)
			m268_reg[{1'b0,prg_ain[1:0]}] <= prg_din ^ {1'b0,prot[3],1'b0,prot[2:1],2'b00,prot[0]};
	end

	if (m2_inv) begin // Inverted M2
		irq_reg[6:1] <= irq_reg[5:0]; // 4 cpu cycle delay for mapper 48

		if (!acclaim) begin // nintendo mapper 'cools down' for 3 inverted M2 cycles
			a12_ctr <= (a12_ctr != 0) ? a12_ctr - 4'b0001 : a12_ctr;
		end
	end

	if (~paused) begin
		// Trigger IRQ counter on rising edge of chr_ain[12]
		// All MMC3A's and non-Sharp MMC3B's will generate only a single IRQ when $C000 is $00.
		// This is because this version of the MMC3 generates IRQs when the scanline counter is decremented to 0.
		// In addition, writing to $C001 with $C000 still at $00 will result in another single IRQ being generated.
		// In the community, this is known as the "alternate" or "old" behavior.
		// All MMC3C's and Sharp MMC3B's will generate an IRQ on each scanline while $C000 is $00.
		// This is because this version of the MMC3 generates IRQs when the scanline counter is equal to 0.
		// In the community, this is known as the "normal" or "new" behavior.

		last_a12 <= chr_ain_o[12];
		if ((acclaim && (!last_a12 && chr_ain_o[12]) && (a12_ctr == 6)) ||
			(~acclaim && (!last_a12 && chr_ain_o[12]) && (a12_ctr == 0))) begin
			counter <= new_counter;

			// MMC Scanline
			if ( (!mmc3_alt_behavior || counter != 0 || irq_reload) && new_counter == 0 && irq_enable && irq_support) begin
				irq_reg[0] <= 1;
			end
			irq_reload <= 0;
		end

		if (acclaim) begin
			if (!last_a12 && chr_ain_o[12]) // acclaim mapper counts down 8 pulses, or 16 edges total
				a12_ctr <= (a12_ctr != 0) ? a12_ctr - 4'b0001 : 4'b0111;
			if (prg_ain == 16'hC001 && prg_write) a12_ctr <= 4'b0111;
		end else if (chr_ain_o[12]) // Nintendo mapper cooldown resets on a12
			a12_ctr <= 4'b0011;
	end
end

assign SS_MAP1_BACK[ 6: 0] = irq_reg;
assign SS_MAP1_BACK[ 9: 7] = bank_select;
assign SS_MAP1_BACK[   10] = prg_rom_bank_mode;
assign SS_MAP1_BACK[   11] = chr_a12_invert;
assign SS_MAP1_BACK[   12] = mirroring;
assign SS_MAP1_BACK[   13] = irq_enable;
assign SS_MAP1_BACK[   14] = irq_reload;
assign SS_MAP1_BACK[22:15] = irq_latch;
assign SS_MAP1_BACK[30:23] = counter;
assign SS_MAP1_BACK[34:31] = ram_enable;
assign SS_MAP1_BACK[38:35] = ram_protect;
assign SS_MAP1_BACK[46:39] = chr_bank_0;
assign SS_MAP1_BACK[54:47] = chr_bank_1;
assign SS_MAP1_BACK[62:55] = chr_bank_2;
assign SS_MAP1_BACK[   63] = 1'b0; // free to be used

assign SS_MAP2_BACK[ 7: 0] = chr_bank_3;
assign SS_MAP2_BACK[15: 8] = chr_bank_4;
assign SS_MAP2_BACK[23:16] = chr_bank_5;
assign SS_MAP2_BACK[29:24] = prg_bank_0[5:0];
assign SS_MAP2_BACK[35:30] = prg_bank_1[5:0];
assign SS_MAP2_BACK[41:36] = prg_bank_2[5:0];
assign SS_MAP2_BACK[45:42] = a12_ctr;
assign SS_MAP2_BACK[   46] = last_a12;
assign SS_MAP2_BACK[49:47] = mapper37_multicart;
assign SS_MAP2_BACK[   50] = mapper47_multicart;
assign SS_MAP2_BACK[54:51] = mapper189_prgsel;
assign SS_MAP2_BACK[   55] = ram6_enabled;
assign SS_MAP2_BACK[   56] = ram6_enable;
assign SS_MAP2_BACK[   57] = ram6_protect;
assign SS_MAP2_BACK[59:58] = prg_bank_0[7:6];
assign SS_MAP2_BACK[61:60] = prg_bank_1[7:6];
assign SS_MAP2_BACK[63:62] = prg_bank_2[7:6];
//assign SS_MAP2_BACK[64:64] = 0'b0; // full

assign SS_MAP3_BACK[ 7: 0] = m268_reg[0];
assign SS_MAP3_BACK[15: 8] = m268_reg[1];
assign SS_MAP3_BACK[23:16] = m268_reg[2];
assign SS_MAP3_BACK[31:24] = m268_reg[3];
assign SS_MAP3_BACK[39:32] = m268_reg[4];
assign SS_MAP3_BACK[47:40] = m268_reg[5];
assign SS_MAP3_BACK[   48] = chr_bank_0_0;
assign SS_MAP3_BACK[   49] = chr_bank_1_0;
assign SS_MAP3_BACK[63:50] = 14'b0; // free to be used

// The PRG bank to load. Each increment here is 8kb. So valid values are 0..63.
reg [7:0] prgsel;
always @* begin
	casez({prg_ain[14:13], prg_rom_bank_mode && prg_invert_support})
		3'b00_0: prgsel = prg_bank_0;  // $8000 mode 0
		3'b00_1: prgsel = prg_bank_2;  // $8000 fixed to second last bank
		3'b01_?: prgsel = prg_bank_1;  // $A000 mode 0,1
		3'b10_0: prgsel = prg_bank_2;  // $C000 fixed to second last bank
		3'b10_1: prgsel = prg_bank_0;  // $C000 mode 1
		3'b11_?: prgsel = 8'b11111111; // $E000 fixed to last bank
	endcase

	// mapper47 is limited to 128k PRG, the top bits are controlled by mapper47_multicart instead.
	if (mapper47) prgsel[7:4] = {3'b000, mapper47_multicart};
	if (mapper37) begin
	prgsel[7:4] = {3'b000, mapper37_multicart[2]};
		if (mapper37_multicart[1:0] == 3'd3)
			prgsel[3] = 1'b1;
		else if (mapper37_multicart[2] == 1'b0)
			prgsel[3] = 1'b0;
	end
	if (mapper205) prgsel[7:4] = {2'b00, mapper37_multicart[1], mapper37_multicart[0] | (prgsel[4] & !mapper37_multicart[1])};

	if (mapper189 || mapper208) prgsel = {2'b00,mapper189_prgsel,prg_ain[14:13]};
	if (!oversized) prgsel[7:6] = 2'b00;
end

// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
reg [8:0] chrsel;
wire use_chr_ain_12 = chr_ain[12] ^ (chr_a12_invert && chr_invert_support);
always @* begin
	if (!mapper76) begin
		casez({use_chr_ain_12, chr_ain[11], chr_ain[10]})
			3'b00?: chrsel = {chr_bank_0, chr_ain[10]};
			3'b01?: chrsel = {chr_bank_1, chr_ain[10]};
			3'b100: chrsel = {1'b0, chr_bank_2};
			3'b101: chrsel = {1'b0, chr_bank_3};
			3'b110: chrsel = {1'b0, chr_bank_4};
			3'b111: chrsel = {1'b0, chr_bank_5};
		endcase
		// mapper47 is limited to 128k CHR, the top bit is controlled by mapper47_multicart instead.
		if (mapper47) chrsel[7] = mapper47_multicart;
		if (mapper37) chrsel[7] = mapper37_multicart[2];
		if (mapper205) chrsel[8:7] = {mapper37_multicart[1], mapper37_multicart[0] | (chrsel[7] & !mapper37_multicart[1])};
		if ((mapper88) || (mapper154)) chrsel[6] = chr_ain[12];
	end else begin
		case(chr_ain[12:11])
			2'b00: chrsel = {chr_bank_2, chr_ain[10]};
			2'b01: chrsel = {chr_bank_3, chr_ain[10]};
			2'b10: chrsel = {chr_bank_4, chr_ain[10]};
			2'b11: chrsel = {chr_bank_5, chr_ain[10]};
		endcase
	end
end

// Read from mapper
always @* begin
	prg_bus_write = 1'b1;
	if (!prg_write && mapper208 && prg_ain[15:11] == 5'b01011) begin // 5800
		prg_dout = m268_reg[{1'b0,prg_ain[1:0]}]; // Warning 10027 suppressed: index is correctly constrained to 0-3
	end else begin
		prg_dout = 8'hFF; // By default open bus.
		prg_bus_write = 0;
	end
end

assign gnrom = m268_reg[3][4];
assign lockout = m268_reg[3][7] && !gnrom;
assign gnrom_lock = m268_reg[2][7];
assign mega_unrom = m268_reg[5][4];
assign weird_mode = m268_reg[3][6];
wire [24:13] map268p; //only [20:13] used below => max size is 2MB
wire [17:10] map268c;
assign map268p[24:21] = {m268_reg[0][5:4],m268_reg[1][3:2]};
assign map268p[20] = m268_reg[1][5]?prgsel[7]:m268_reg[1][4];
assign map268p[19] = mega_unrom ? (prg_ain[14] | map268c[17]): m268_reg[1][6]?prgsel[6]:m268_reg[0][2];
assign map268p[18] = mega_unrom ? (prg_ain[14] | map268c[16]): !m268_reg[1][7]?prgsel[5]:m268_reg[0][1];
assign map268p[17] = !m268_reg[0][6]?prgsel[4]:m268_reg[0][0];
assign map268p[16:15] = gnrom ? m268_reg[3][3:2] : weird_mode && !prg_rom_bank_mode && prg_ain[14]?2'b00:prgsel[3:2];
assign map268p[14] = gnrom ? m268_reg[1][1]?prg_ain[14]:m268_reg[3][1] : weird_mode && !prg_rom_bank_mode && prg_ain[14]?1'b0:prgsel[1];
assign map268p[13] = gnrom ? prg_ain[13]: weird_mode && !prg_rom_bank_mode && prg_ain[14]?1'b0:prgsel[0];

assign map268c[17] = !m268_reg[0][7]?chrsel[7]:m268_reg[0][3];
assign map268c[16:13] = gnrom ? ({~m268_reg[2][6:4],1'b1} & m268_reg[2][3:0]): weird_mode && chr_ain[10] ? 4'h0 : chrsel[6:3];
assign map268c[12:11] = (weird_mode && chr_ain[10]) ? 2'h0 : chrsel[2:1];
assign map268c[10] = (weird_mode && chr_ain[10]) ? 1'b0 : (use_chr_ain_12 || !weird_mode) ? chrsel[0] : chr_ain[12] ? chr_bank_1_0 : chr_bank_0_0 ;

wire m268_chr_ram = {map268c[17:11],1'b1} == m268_reg[4];

wire [21:0] prg_aout_tmp = {1'b0, mapper268 ? map268p[20:13] : prgsel, prg_ain[12:0]};

wire ram_enable_a = !MMC6 ? (ram_enable[prg_ain[12:11]])
						:   (ram6_enabled && ram6_enable && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b0)
						 || (ram6_enabled && ram_enable[3] && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b1);

wire ram_protect_a = !MMC6 ? (ram_protect[prg_ain[12:11]])
						:   !(ram6_enabled && ram6_enable && ram6_protect && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b0)
						 && !(ram6_enabled && ram_enable[3] && ram_protect[3] && prg_ain[12] == 1'b1 && prg_ain[9] == 1'b1);

wire chr_ram_cs =
		TQROM                 ? chrsel[6]               :
		mapper74              ? chrsel[7:1]==7'b0000100 :
		mapper191             ? chrsel[7]               :
		mapper192             ? chrsel[7:2]==6'b000010  :
		mapper194             ? chrsel[7:1]==7'b0000000 :
		mapper195             ? chrsel[7:2]==6'b000000  :
		flags[15];

assign chr_allow = chr_ram_cs | (four_screen_mirroring & chr_ain[13]);
assign chr_aout =
		(four_screen_mirroring & chr_ain[13])? {10'b11_1111_1100,              chr_ain[11:0]} :   // DxROM/TVROM 4kb NT RAM
		(TQROM & chr_ram_cs)                 ? {9'b11_1111_111,    chrsel[2:0], chr_ain[9:0]} :   // TQROM 8kb CHR-RAM
		(mapper74 & chr_ram_cs)              ? {11'b11_1111_1111_1,chrsel[0],   chr_ain[9:0]} :   // 2kb CHR-RAM
		(mapper191 & chr_ram_cs)             ? {11'b11_1111_1111_1,chrsel[0],   chr_ain[9:0]} :   // 2kb CHR-RAM
		(mapper192 & chr_ram_cs)             ? {10'b11_1111_1111,  chrsel[1:0], chr_ain[9:0]} :   // 4kb CHR-RAM
		(mapper194 & chr_ram_cs)             ? {11'b11_1111_1111_1,chrsel[0],   chr_ain[9:0]} :   // 2kb CHR-RAM
		(mapper195 & chr_ram_cs)             ? {10'b11_1111_1111,  chrsel[1:0], chr_ain[9:0]} :   // 4kb CHR-RAM
		(m268_chr_ram)                       ? {11'b11_1111_1111_1,            chr_ain[10:0]} :   // 2kb CHR-RAM
		(mapper268)                          ? {4'b10_00,              map268c, chr_ain[9:0]} :   // Mapper 268 override
		                                       {3'b10_0,                chrsel, chr_ain[9:0]};    // Standard MMC3 CHR-ROM/RAM

wire ram_a13 = mapper268 && m268_reg[3][5] && (prg_ain[15:12] == 4'h5);
assign prg_is_ram = (ram_a13 || (prg_ain[15:13] == 3'b011) && ((prg_ain[12:8] == 5'b1_1111) | ~internal_128)) //(>= 'h6000 && < 'h8000) && (==7Fxx or external_ram)
					&& ram_enable_a && !(ram_protect_a && prg_write);
assign prg_allow = prg_ain[15] && !prg_write || (prg_is_ram && !mapper47 && !mapper208);
wire [21:0] prg_ram = {8'b11_1100_00, ram_a13, internal_128 ? 6'b000000 : MMC6 ? {3'b000, prg_ain[9:7]} : prg_ain[12:7], prg_ain[6:0]};
assign prg_aout = prg_is_ram  && !mapper47 && !mapper208 && !DxROM && !mapper95 && !mapper88 ? prg_ram : prg_aout_tmp;
assign vram_a10 = TxSROM ? chrsel[7] :              // TxSROM do not support mirroring
					mapper95 ? chrsel[5] :          // mapper95 does not support mirroring
					mapper154 ? mirroring :         // mapper154 does not support mirroring
					mapper207 ? chrsel[7] :         // mapper207 does not support mirroring
					(mirroring ? chr_ain[10] : chr_ain[11]);
assign vram_ce = chr_ain[13] && !four_screen_mirroring;

// savestate
localparam SAVESTATE_MODULES    = 3;
wire [63:0] SaveStateBus_wired_or[0:SAVESTATE_MODULES-1];
wire [63:0] SS_MAP1, SS_MAP2, SS_MAP3;
wire [63:0] SS_MAP1_BACK, SS_MAP2_BACK, SS_MAP3_BACK;
wire [63:0] SaveStateBus_Dout_active = SaveStateBus_wired_or[0] | SaveStateBus_wired_or[1] | SaveStateBus_wired_or[2];

eReg_SavestateV #(SSREG_INDEX_MAP1, 64'h0000000000000000) iREG_SAVESTATE_MAP1 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[0], SS_MAP1_BACK, SS_MAP1);
eReg_SavestateV #(SSREG_INDEX_MAP2, 64'h0000000000000000) iREG_SAVESTATE_MAP2 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[1], SS_MAP2_BACK, SS_MAP2);
eReg_SavestateV #(SSREG_INDEX_MAP3, 64'h0000000000000000) iREG_SAVESTATE_MAP3 (clk, SaveStateBus_Din, SaveStateBus_Adr, SaveStateBus_wren, SaveStateBus_rst, SaveStateBus_wired_or[2], SS_MAP3_BACK, SS_MAP3);

assign SaveStateBus_Dout = enable ? SaveStateBus_Dout_active : 64'h0000000000000000;

endmodule
// altera message_on 10027

// mapper 165
module Mapper165(
	input        clk,         // System clock
	input        ce,          // M2 ~cpu_clk
	input        enable,      // Mapper enabled
	input [31:0] flags,       // Cart flags
	input [15:0] prg_ain,     // prg address
	inout [21:0] prg_aout_b,  // prg address out
	input        prg_read,    // prg read
	input        prg_write,   // prg write
	input  [7:0] prg_din,     // prg data in
	inout  [7:0] prg_dout_b,  // prg data out
	inout        prg_allow_b, // Enable access to memory for the specified operation.
	input [13:0] chr_ain,     // chr address in
	inout [21:0] chr_aout_b,  // chr address out
	input        chr_read,    // chr ram read
	inout        chr_allow_b, // chr allow write
	inout        vram_a10_b,  // Value for A10 address line
	inout        vram_ce_b,   // True if the address should be routed to the internal 2kB VRAM.
	inout        irq_b,       // IRQ
	input [15:0] audio_in,    // Inverted audio from APU
	inout [15:0] audio_b,     // Mixed audio output
	inout [15:0] flags_out_b, // flags {0, 0, 0, 0, has_savestate, prg_conflict, prg_bus_write, has_chr_dout}
	input [13:0] chr_ain_o,
	input        m2_inv,
	input        paused
);

assign prg_aout_b   = enable ? prg_aout : 22'hZ;
assign prg_dout_b   = enable ? prg_dout : 8'hZ;
assign prg_allow_b  = enable ? prg_allow : 1'hZ;
assign chr_aout_b   = enable ? chr_aout : 22'hZ;
assign chr_allow_b  = enable ? chr_allow : 1'hZ;
assign vram_a10_b   = enable ? vram_a10 : 1'hZ;
assign vram_ce_b    = enable ? vram_ce : 1'hZ;
assign irq_b        = enable ? irq : 1'hZ;
assign flags_out_b  = enable ? flags_out : 16'hZ;
assign audio_b      = enable ? {1'b0, audio_in[15:1]} : 16'hZ;

wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout = 0;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
reg irq;
reg [15:0] flags_out = 0;

reg [2:0] bank_select;             // Register to write to next
reg prg_rom_bank_mode;             // Mode for PRG banking
reg chr_a12_invert;                // Mode for CHR banking
reg mirroring;                     // 0 = vertical, 1 = horizontal
reg irq_enable, irq_reload;        // IRQ enabled, and IRQ reload requested
reg [7:0] irq_latch, counter;      // IRQ latch value and current counter
reg ram_enable, ram_protect;       // RAM protection bits
reg [5:0] prg_bank_0, prg_bank_1;  // Selected PRG banks
wire prg_is_ram;

reg [7:0] chr_bank_0, chr_bank_1;  // Selected CHR banks
reg [7:0] chr_bank_2, chr_bank_4;
reg latch_0, latch_1;

wire [7:0] new_counter = (counter == 0 || irq_reload) ? irq_latch : counter - 1'd1;
reg [3:0] a12_ctr;
reg last_a12 = 0;

always @(posedge clk)
if (~enable) begin
	irq <= 0;
	bank_select <= 0;
	prg_rom_bank_mode <= 0;
	chr_a12_invert <= 0;
	mirroring <= flags[14];
	{irq_enable, irq_reload} <= 0;
	{irq_latch, counter} <= 0;
	{ram_enable, ram_protect} <= 0;
	{chr_bank_0, chr_bank_1, chr_bank_2, chr_bank_4} <= 0;
	{prg_bank_0, prg_bank_1} <= 0;
	a12_ctr <= 0;
	last_a12 <= 0;
end else begin
	if (ce) begin
		if (prg_write && prg_ain[15]) begin
			case({prg_ain[14], prg_ain[13], prg_ain[0]})
				3'b00_0: {chr_a12_invert, prg_rom_bank_mode, bank_select} <= {prg_din[7], prg_din[6], prg_din[2:0]}; // Bank select ($8000-$9FFE, even)
				3'b00_1: begin // Bank data ($8001-$9FFF, odd)
					case (bank_select)
						0: chr_bank_0 <= {prg_din[7:1], 1'b0};  // Select 2 KB CHR bank at PPU $0000-$07FF (or $1000-$17FF);
						1: chr_bank_1 <= {prg_din[7:1], 1'b0};  // Select 2 KB CHR bank at PPU $0800-$0FFF (or $1800-$1FFF);
						2: chr_bank_2 <= prg_din;       // Select 1 KB CHR bank at PPU $1000-$13FF (or $0000-$03FF);
						3: ;                            // Select 1 KB CHR bank at PPU $1400-$17FF (or $0400-$07FF);
						4: chr_bank_4 <= prg_din;       // Select 1 KB CHR bank at PPU $1800-$1BFF (or $0800-$0BFF);
						5: ;                            // Select 1 KB CHR bank at PPU $1C00-$1FFF (or $0C00-$0FFF);
						6: prg_bank_0 <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $8000-$9FFF (or $C000-$DFFF);
						7: prg_bank_1 <= prg_din[5:0];  // Select 8 KB PRG ROM bank at $A000-$BFFF
					endcase
				end
				3'b01_0: mirroring <= prg_din[0];                   // Mirroring ($A000-$BFFE, even)
				3'b01_1: {ram_enable, ram_protect} <= prg_din[7:6]; // PRG RAM protect ($A001-$BFFF, odd)
				3'b10_0: irq_latch <= prg_din;                      // IRQ latch ($C000-$DFFE, even)
				3'b10_1: irq_reload <= 1;                           // IRQ reload ($C001-$DFFF, odd)
				3'b11_0: begin irq_enable <= 0; irq <= 0; end       // IRQ disable ($E000-$FFFE, even)
				3'b11_1: irq_enable <= 1;                           // IRQ enable ($E001-$FFFF, odd)
			endcase
		end
	end

	if (m2_inv) begin // Inverted M2
		// nintendo mapper 'cools down' for 3 inverted M2 cycles
		a12_ctr <= (a12_ctr != 0) ? a12_ctr - 4'b0001 : a12_ctr;
	end

	if (~paused) begin
		// Trigger IRQ counter on rising edge of chr_ain[12]

		last_a12 <= chr_ain_o[12];
		if ((!last_a12 && chr_ain_o[12]) && (a12_ctr == 0)) begin
			counter <= new_counter;

			// MMC Scanline
			if ( (counter != 0 || irq_reload) && new_counter == 0 && irq_enable) begin
				irq <= 1;
			end
			irq_reload <= 0;
		end

		if (chr_ain_o[12]) // Nintendo mapper cooldown resets on a12
			a12_ctr <= 4'b0011;
	end
end

// The PRG bank to load. Each increment here is 8kb. So valid values are 0..63.
reg [5:0] prgsel;
always @* begin
	casez({prg_ain[14:13], prg_rom_bank_mode})
		3'b00_0: prgsel = prg_bank_0;  // $8000 mode 0
		3'b00_1: prgsel = 6'b111110;   // $8000 fixed to second last bank
		3'b01_?: prgsel = prg_bank_1;  // $A000 mode 0,1
		3'b10_0: prgsel = 6'b111110;   // $C000 fixed to second last bank
		3'b10_1: prgsel = prg_bank_0;  // $C000 mode 1
		3'b11_?: prgsel = 6'b111111;   // $E000 fixed to last bank
	endcase
end

wire [21:0] prg_aout_tmp = {3'b00_0,  prgsel, prg_ain[12:0]};

// PPU reads $0FD0: latch 0 is set to $FD for subsequent reads
// PPU reads $0FE0: latch 0 is set to $FE for subsequent reads
// PPU reads $1FD0 through $1FDF: latch 1 is set to $FD for subsequent reads
// PPU reads $1FE0 through $1FEF: latch 1 is set to $FE for subsequent reads
always @(posedge clk)
if (ce && chr_read) begin
	latch_0 <= (chr_ain_o       == 14'h0fd0) ? 1'd0 : (chr_ain_o       == 14'h0fe0) ? 1'd1 : latch_0;
	latch_1 <= (chr_ain_o[13:4] == 10'h1fd ) ? 1'd0 : (chr_ain_o[13:4] == 10'h1fe ) ? 1'd1 : latch_1;
end

// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
reg [7:0] chrsel;
always @* begin
	casez({chr_ain[12] ^ chr_a12_invert, latch_0, latch_1})
		3'b0_0?: chrsel = chr_bank_0;
		3'b0_1?: chrsel = chr_bank_1;
		3'b1_?0: chrsel = chr_bank_2;
		3'b1_?1: chrsel = chr_bank_4;
	endcase
end

assign chr_allow = !chrsel; // page 0 is CHR-RAM
assign chr_aout = !chrsel ? {10'b11_1111_1111, chr_ain[11:0]} :   // 4KB CHR-RAM
				{4'b10_00, chrsel[7:2], chr_ain[11:0]}; // CHR-ROM per 4KB page

assign prg_is_ram = prg_ain >= 'h6000 && prg_ain < 'h8000 && ram_enable && !(ram_protect && prg_write);
assign prg_allow = prg_ain[15] && !prg_write || prg_is_ram;
wire [21:0] prg_ram = {9'b11_1100_000, prg_ain[12:0]};
assign prg_aout = prg_is_ram ? prg_ram : prg_aout_tmp;
assign vram_a10 = mirroring ? chr_ain[11] : chr_ain[10];
assign vram_ce = chr_ain[13];

endmodule

// mapper 413 based on Nintendulator NRS source
// mapper 413 uses MMC3 style interrupts
module Mapper413 (
	input        clk,         // System clock
	input        ce,          // M2 ~cpu_clk
	input        enable,      // Mapper enabled
	input [31:0] flags,       // Cart flags
	input [15:0] prg_ain,     // prg address
	inout [21:0] prg_aout_b,  // prg address out
	input        prg_read,    // prg read
	input        prg_write,   // prg write
	input  [7:0] prg_din,     // prg data in
	inout  [7:0] prg_dout_b,  // prg data out
	inout        prg_allow_b, // Enable access to memory for the specified operation.
	input [13:0] chr_ain,     // chr address in
	inout [21:0] chr_aout_b,  // chr address out
	input        chr_read,    // chr ram read
	inout        chr_allow_b, // chr allow write
	inout        vram_a10_b,  // Value for A10 address line
	inout        vram_ce_b,   // True if the address should be routed to the internal 2kB VRAM.
	inout        irq_b,       // IRQ
	input [15:0] audio_in,    // Inverted audio from APU
	inout [15:0] audio_b,     // Mixed audio output
	inout [15:0] flags_out_b, // flags {0, 0, 0, 0, has_savestate, prg_conflict, prg_bus_write, has_chr_dout}
	input [13:0] chr_ain_o,
	input        m2_inv,
	input        paused,
	output [2:0] prg_aoute    // Extended prg address out bits
);

assign prg_aout_b   = enable ? prg_aout : 22'hZ;
assign prg_dout_b   = enable ? prg_dout : 8'hZ;
assign prg_allow_b  = enable ? prg_allow : 1'hZ;
assign chr_aout_b   = enable ? chr_aout : 22'hZ;
assign chr_allow_b  = enable ? chr_allow : 1'hZ;
assign vram_a10_b   = enable ? vram_a10 : 1'hZ;
assign vram_ce_b    = enable ? vram_ce : 1'hZ;
assign irq_b        = enable ? irq : 1'hZ;
assign flags_out_b  = enable ? flags_out : 16'hZ;
assign audio_b      = enable ? {1'b0, audio_in[15:1]} : 16'hZ;

wire [21:0] prg_aout, chr_aout;
wire [7:0] prg_dout = 0;
wire prg_allow;
wire chr_allow;
wire vram_a10;
wire vram_ce;
wire irq;
reg [15:0] flags_out = 0;

reg irq_enable, irq_reload;        // IRQ enabled, and IRQ reload requested
reg [7:0] irq_latch, counter;      // IRQ latch value and current counter
reg [4:0] irq_reg;
assign irq = irq_reg[0];

wire [7:0] new_counter = (counter == 0 || irq_reload) ? irq_latch : counter - 1'd1;
reg [3:0] a12_ctr;
reg last_a12 = 0;

reg misc_inc;
reg old_misc_inc;
reg misc_ctrl;
reg [22:0] prg_amisc;
wire prg_is_misc = (prg_ain[15:11] == 5'b0100_1) | (prg_ain[15:12] == 4'b1100); // $48xx or $Cxxx
assign prg_aoute = prg_is_misc ? {2'b10,prg_amisc[22]} : 3'd0;

reg [5:0] bank_reg[0:3];

always @(posedge clk)
if (~enable) begin
	irq_reg <= 5'b00000;
	{irq_enable, irq_reload} <= 0;
	{irq_latch, counter} <= 0;
	bank_reg[0] <= 0;
	bank_reg[1] <= 0;
	bank_reg[2] <= 0;
	bank_reg[3] <= 0;
	prg_amisc <= 0;
	misc_ctrl <= 0;
	misc_inc <= 0;
	old_misc_inc <= 0;
	a12_ctr <= 0;
	last_a12 <= 0;
end else begin
	if (ce) begin
		if (prg_write && prg_ain[15]) begin
			casez(prg_ain[14:12])
				3'b000: irq_latch <= prg_din;                      // IRQ latch $8000-8FFF (mmc3=$C000-$DFFE, even)
				3'b001: irq_reload <= 1;                           // IRQ reload $9000-9FFF (mmc3=$C001-$DFFF, odd)
				3'b010: begin irq_enable <= 0; irq_reg[0] <= 0; end// IRQ disable $A000-AFFF (mmc3=$E000-$FFFE, even)
				3'b011: irq_enable <= 1;                           // IRQ enable $B000-BFFF (mmc3=$E001-$FFFF, odd)
				3'b100: prg_amisc <= {prg_amisc[21:0], prg_din[7]};// Misc Address (shift left 1, insert MSB)
				3'b101: misc_ctrl <= prg_din[1];                   // Misc Control (& 0x2)
				3'b11?: bank_reg[prg_din[7:6]] <= prg_din[5:0];    // Bank select din[7:6] ($E000-$EFFE)
			endcase
		end

		misc_inc <= 0;
		old_misc_inc <= misc_inc;
		if (prg_read && prg_is_misc)
			misc_inc <= 1;
		if (old_misc_inc && !misc_inc && misc_ctrl)
			prg_amisc <= prg_amisc + 23'd1;
	end

	if (m2_inv) begin // Inverted M2
		// nintendo mapper 'cools down' for 3 inverted M2 cycles
		a12_ctr <= (a12_ctr != 0) ? a12_ctr - 4'b0001 : a12_ctr;
		irq_reg[4:1] <= irq_reg[3:0]; // 1 cpu cycle delay ??? what is this for
	end

	if (~paused) begin
		// Trigger IRQ counter on rising edge of chr_ain[12]

		last_a12 <= chr_ain_o[12];
		if ((!last_a12 && chr_ain_o[12]) && (a12_ctr == 0)) begin
			counter <= new_counter;

			// MMC Scanline
			if ( (counter != 0 || irq_reload) && new_counter == 0 && irq_enable) begin
				irq_reg[0] <= 1;
			end
			irq_reload <= 0;
		end

		if (chr_ain_o[12]) // Nintendo mapper cooldown resets on a12
			a12_ctr <= 4'b0011;
	end
end

reg [5:0] prgsel;
always @* begin
	casez(prg_ain[15:11])
		5'b00??_?: prgsel = 6'b000000;   // $0000 CPU - not used
//		5'b0100_0: prgsel = 6'b000000;   // $4000 not used
//		5'b0100_1: prgsel = 6'b000000;   // $4800 misc - not used
		5'b010?_?: prgsel = 6'b000000;   // $5000 fixed to 0x1 (prg_ain[11]=1)
		5'b011?_?: prgsel = bank_reg[0]; // $6000 =$E000, $00
		5'b100?_?: prgsel = bank_reg[1]; // $8000 =$E000, $40
		5'b101?_?: prgsel = bank_reg[2]; // $A000 =$E000, $80
//		5'b1100_?: prgsel = 6'b000011;   // $C000 misc - not used
		5'b110?_?: prgsel = 6'b000011;   // $D000 fixed to 0x7 ({prgsel[5:0],prg_ain[11]} = 7)
		5'b111?_?: prgsel = 6'b000100;   // $E000 fixed to 0x8-9
	endcase
end

// The CHR bank to load. Each increment here is 1kb. So valid values are 0..255.
reg [5:0] chrsel;
always @* begin
	case(chr_ain[12])
		1'b0: chrsel = bank_reg[3]; // =$E000, $C0
		1'b1: chrsel = 6'b11_1101;  // 0x3D
	endcase
end

wire [21:0] prg_aout_tmp = {3'b00_0,  prgsel, prg_ain[12:0]};

assign chr_allow = flags[15];
assign chr_aout =  {4'b10_00, chrsel, chr_ain[11:0]};

assign prg_allow = (prg_ain[15] || (prg_ain[14] && (prg_ain[13:11] != 3'b000))) && !prg_write;
assign prg_aout = prg_is_misc ? prg_amisc[21:0] : prg_aout_tmp;
assign vram_a10 = flags[14] ? chr_ain[10] : chr_ain[11];
assign vram_ce = chr_ain[13];

endmodule

9: chr_bank_9 <= prg_din;       // If K=1, Select 1 KB CHR bank at PPU $0C00 (or $1C00)


`include "bsg_defines.sv"

module bsg_zynq_pl_shell #
  (
   //--------------------------------------------------------------------------------
   // USER MODIFY -- Configure your accelerator interface by setting these parameters
   //--------------------------------------------------------------------------------
   //
   // Change these parameters to change the number of CSRs, input FIFOs and output FIFOs.
   //
   // note: we automatically create a "elements avail" CSR for the input FIFO
   //       and a "free space avail" csr for each output FIFO
   //
   // if the PS reads from the FIFO and it is empty, it will return bogus data.
   // if the PS writes to a FIFO and it is full, it will be dropped.
   //
   // reading the FIFO csrs tells you how many consecutive words you can read/write before
   // checking the csr again

   parameter  num_regs_ps_to_pl_p = 1      // number of user CSRs read-only by PL, r/w by PS
   ,parameter num_regs_pl_to_ps_p = 1      // number of user CSRs read-only by PS, r/w by PL
   ,parameter num_fifo_ps_to_pl_p = 1      // number of input FIFOs (from PS to PL)
   ,parameter num_fifo_pl_to_ps_p = 1      // number of output FIFOs (from PL to PS)

   // this correspond to the number of word addresses that can be read by PS
   ,parameter read_locs_lp = num_regs_ps_to_pl_p+num_fifo_pl_to_ps_p+num_fifo_ps_to_pl_p+num_fifo_pl_to_ps_p+num_regs_pl_to_ps_p

   // this corresponds to the number of addresses that can be written by PS
   ,parameter write_locs_lp = num_regs_ps_to_pl_p+num_fifo_ps_to_pl_p

   // Width of S_AXI data bus

   ,parameter integer C_S_AXI_DATA_WIDTH   = 32

   // Width of S_AXI address bus

   // local parameter for addressing 32 bit / 64 bit C_S_AXI_DATA_WIDTH
   // ADDR_LSB is used for addressing 32/64 bit registers/memories
   // ADDR_LSB = 2 for 32 bits (n downto 2)
   // ADDR_LSB = 3 for 64 bits (n downto 3)

   ,localparam integer ADDR_LSB_lp = (C_S_AXI_DATA_WIDTH/32) + 1
   ,parameter integer C_S_AXI_ADDR_WIDTH   = `BSG_SAFE_CLOG2(read_locs_lp)+ADDR_LSB_lp
   )
   (
    input wire [num_fifo_pl_to_ps_p-1:0][C_S_AXI_DATA_WIDTH-1:0]  pl_to_ps_fifo_data_i,
    input wire [num_fifo_pl_to_ps_p-1:0]                          pl_to_ps_fifo_v_i,
    output wire [num_fifo_pl_to_ps_p-1:0]                         pl_to_ps_fifo_ready_o,

    output wire [num_fifo_ps_to_pl_p-1:0][C_S_AXI_DATA_WIDTH-1:0] ps_to_pl_fifo_data_o,
    output wire [num_fifo_ps_to_pl_p-1:0]                         ps_to_pl_fifo_v_o,
    input wire [num_fifo_ps_to_pl_p-1:0]                          ps_to_pl_fifo_yumi_i,

    output wire [num_regs_ps_to_pl_p-1:0]                         csr_data_new_o,
    output wire [num_regs_ps_to_pl_p-1:0][C_S_AXI_DATA_WIDTH-1:0] csr_data_o,

    // note: this does not create registers, they are implemented in client PL
    input wire [num_regs_pl_to_ps_p-1:0][C_S_AXI_DATA_WIDTH-1:0]  csr_data_i,

                // Global Clock Signal
    input wire                                                    S_AXI_ACLK,
                // Global Reset Signal. This Signal is Active LOW
    input wire                                                    S_AXI_ARESETN,
                // Write address (issued by master, acceped by Slave)
    input wire [C_S_AXI_ADDR_WIDTH-1 : 0]                         S_AXI_AWADDR,
                // Write channel Protection type. This signal indicates the
                // privilege and security level of the transaction, and whether
                // the transaction is a data access or an instruction access.
    input wire [2 : 0]                                            S_AXI_AWPROT,
                // Write address valid. This signal indicates that the master signaling
                // valid write address and control information.
    input wire                                                    S_AXI_AWVALID,
                // Write address ready. This signal indicates that the slave is ready
                // to accept an address and associated control signals.
    output wire                                                   S_AXI_AWREADY,
                // Write data (issued by master, acceped by Slave)
    input wire [C_S_AXI_DATA_WIDTH-1 : 0]                         S_AXI_WDATA,
                // Write strobes. This signal indicates which byte lanes hold
                // valid data. There is one write strobe bit for each eight
                // bits of the write data bus.
    input wire [(C_S_AXI_DATA_WIDTH/8)-1 : 0]                     S_AXI_WSTRB,
                // Write valid. This signal indicates that valid write
                // data and strobes are available.
    input wire                                                    S_AXI_WVALID,
                // Write ready. This signal indicates that the slave
                // can accept the write data.
    output wire                                                   S_AXI_WREADY,
                // Write response. This signal indicates the status
                // of the write transaction.
    output wire [1 : 0]                                           S_AXI_BRESP,
                // Write response valid. This signal indicates that the channel
                // is signaling a valid write response.
    output wire                                                   S_AXI_BVALID,
                // Response ready. This signal indicates that the master
                // can accept a write response.
    input wire                                                    S_AXI_BREADY,
                // Read address (issued by master, acceped by Slave)
    input wire [C_S_AXI_ADDR_WIDTH-1 : 0]                         S_AXI_ARADDR,
                // Protection type. This signal indicates the privilege
                // and security level of the transaction, and whether the
                // transaction is a data access or an instruction access.
    input wire [2 : 0]                                            S_AXI_ARPROT,
                // Read address valid. This signal indicates that the channel
                // is signaling valid read address and control information.
    input wire                                                    S_AXI_ARVALID,
                // Read address ready. This signal indicates that the slave is
                // ready to accept an address and associated control signals.
    output wire                                                   S_AXI_ARREADY,
                // Read data (issued by slave)
    output wire [C_S_AXI_DATA_WIDTH-1 : 0]                        S_AXI_RDATA,
                // Read response. This signal indicates the status of the
                // read transfer.
    output wire [1 : 0]                                           S_AXI_RRESP,
                // Read valid. This signal indicates that the channel is
                // signaling the required read data.
    output wire                                                   S_AXI_RVALID,
                // Read ready. This signal indicates that the master can
                // accept the read data and response information.
    input wire                                                    S_AXI_RREADY
    );

   // AXI4LITE signals
   reg [C_S_AXI_ADDR_WIDTH-1 : 0]                                 axi_awaddr;
   reg                                                            axi_awready;
   reg                                                            axi_wready;
   reg [1 : 0]                                                    axi_bresp;
   reg                                                            axi_bvalid;
   reg [C_S_AXI_ADDR_WIDTH-1 : 0]                                 axi_araddr;
   reg                                                            axi_arready;
   reg [C_S_AXI_DATA_WIDTH-1 : 0]                                 axi_rdata;
   reg [1 : 0]                                                    axi_rresp;
   reg                                                            axi_rvalid;

   reg [num_regs_ps_to_pl_p-1:0] csr_data_new_r;

   if (`BSG_SAFE_CLOG2(read_locs_lp)+ADDR_LSB_lp > C_S_AXI_ADDR_WIDTH)
     $error("read_locs_lp (%d) + ADDR_LSB_lp (%d) ! <= C_S_AXI_ADDR_WIDTH (%d)\n",`BSG_SAFE_CLOG2(read_locs_lp),ADDR_LSB_lp, C_S_AXI_ADDR_WIDTH);

   wire     slv_reg_rden;
   wire     slv_reg_wren;
   reg [C_S_AXI_DATA_WIDTH-1:0] reg_data_out;
   integer                      byte_index;
   reg                          aw_en;

   // I/O Connections assignments

   assign S_AXI_AWREADY    = axi_awready;
   assign S_AXI_WREADY     = axi_wready;
   assign S_AXI_BRESP      = axi_bresp;
   assign S_AXI_BVALID     = axi_bvalid;
   assign S_AXI_ARREADY    = axi_arready;
   assign S_AXI_RDATA      = axi_rdata;
   assign S_AXI_RRESP      = axi_rresp;
   assign S_AXI_RVALID     = axi_rvalid;
   // Implement axi_awready generation
   // axi_awready is asserted for one S_AXI_ACLK clock cycle when both
   // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_awready is
   // de-asserted when reset is low.

   always @( posedge S_AXI_ACLK )
     begin
        if ( S_AXI_ARESETN == 1'b0 )
          begin
             axi_awready <= 1'b0;
             aw_en <= 1'b1;
          end
        else
          begin
             if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
               begin
                  // slave is ready to accept write address when
                  // there is a valid write address and write data
                  // on the write address and data bus. This design
                  // expects no outstanding transactions.
                  axi_awready <= 1'b1;
                  aw_en <= 1'b0;
               end
             else if (S_AXI_BREADY && axi_bvalid)
               begin
                  aw_en <= 1'b1;
                  axi_awready <= 1'b0;
               end
             else
               begin
                  axi_awready <= 1'b0;
               end
          end
     end

   // Implement axi_awaddr latching
   // This process is used to latch the address when both
   // S_AXI_AWVALID and S_AXI_WVALID are valid.

   always @( posedge S_AXI_ACLK )
     begin
        if ( S_AXI_ARESETN == 1'b0 )
            begin
               axi_awaddr <= 0;
            end
        else
          begin
             if (~axi_awready && S_AXI_AWVALID && S_AXI_WVALID && aw_en)
               begin
                  // Write Address latching
                  axi_awaddr <= S_AXI_AWADDR;
               end
          end
        end

   // Implement axi_wready generation
   // axi_wready is asserted for one S_AXI_ACLK clock cycle when both
   // S_AXI_AWVALID and S_AXI_WVALID are asserted. axi_wready is
   // de-asserted when reset is low.

   always @( posedge S_AXI_ACLK )
     begin

          begin
             axi_wready <= 1'b0;
          end
          else
            begin
               if (~axi_wready && S_AXI_WVALID && S_AXI_AWVALID && aw_en )
                 begin
                    // slave is ready to accept write data when
                    // there is a valid write address and write data
                    // on the write address and data bus. This design
                    // expects no outstanding transactions.
                    axi_wready <= 1'b1;
                 end
               else
                 begin
                    axi_wready <= 1'b0;
                 end
            end
     end

   // Implement memory mapped register select and write logic generation
   // The write data is accepted and written to memory mapped registers when
   // axi_awready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted. Write strobes are used to
   // select byte enables of slave registers while writing.
   // These registers are cleared when reset (active low) is applied.
   // Slave register write enable is asserted when valid address and data are available
   // and the slave is ready to accept the write address and write data.
   assign slv_reg_wren = axi_wready && S_AXI_WVALID && axi_awready && S_AXI_AWVALID;

   wire [read_locs_lp-1:0] slv_rd_sel_one_hot;
   wire [write_locs_lp-1:0] slv_wr_sel_one_hot;

   bsg_decode_with_v #(.num_out_p(read_locs_lp)) decode_rd
     (.i(axi_araddr[`BSG_SAFE_CLOG2(read_locs_lp)+ADDR_LSB_lp-1:ADDR_LSB_lp])
      ,.v_i(slv_reg_rden)
      ,.o(slv_rd_sel_one_hot)
      );

   // for num_regs_ps_to_pl_p=4 and num_fifo_ps_to_pl_p=4, the write memory map is essentially:
   //
   // 0,4,8,C: registers
   // 10,14,18,1C: input fifo

   bsg_decode_with_v #(.num_out_p(write_locs_lp)) decode_wr
     (.i(axi_awaddr[`BSG_SAFE_CLOG2(write_locs_lp)+ADDR_LSB_lp-1:ADDR_LSB_lp])
      ,.v_i(slv_reg_wren)
      ,.o(slv_wr_sel_one_hot)
      );

   logic [num_regs_ps_to_pl_p-1:0][C_S_AXI_DATA_WIDTH-1:0] slv_r;

   // instantiate user read/write CSRs
   for (genvar k=0; k < num_regs_ps_to_pl_p; k++)
     begin: rof
        bsg_dff_reset_en #(.width_p(C_S_AXI_DATA_WIDTH)) slv_reg
          (.clk_i(S_AXI_ACLK)
           ,.reset_i(~S_AXI_ARESETN)
           ,.en_i(slv_wr_sel_one_hot[k])
           ,.data_i(S_AXI_WDATA)
           ,.data_o(slv_r[k])
           );
     end

   wire [num_fifo_ps_to_pl_p-1:0] ps_to_pl_fifo_ready_lo, ps_to_pl_fifo_valid_lo, ps_to_pl_fifo_yumi_li, ps_to_pl_fifo_valid_li;
   wire [num_fifo_ps_to_pl_p-1:0][C_S_AXI_DATA_WIDTH-1:0] ps_to_pl_fifo_data_lo;
   wire [num_fifo_ps_to_pl_p-1:0][`BSG_WIDTH(4)-1:0]      ps_to_pl_fifo_ctrs;
   wire [num_fifo_ps_to_pl_p-1:0][C_S_AXI_DATA_WIDTH-1:0] ps_to_pl_fifo_ctrs_full;

   // instantiate in (PS to PL) FIFOs
   for (genvar k=0; k < num_fifo_ps_to_pl_p; k++)
     begin: rof2
        assign ps_to_pl_fifo_ctrs_full[k] = (C_S_AXI_DATA_WIDTH) ' (ps_to_pl_fifo_ctrs[k]);

        assign ps_to_pl_fifo_valid_li[k] = ps_to_pl_fifo_ready_lo[k] & slv_wr_sel_one_hot[num_regs_ps_to_pl_p+k];

        bsg_fifo_1r1w_small #(.width_p(C_S_AXI_DATA_WIDTH), .els_p(4)) fifo
          (.clk_i(S_AXI_ACLK)
           ,.reset_i(~S_AXI_ARESETN)
           ,.v_i(ps_to_pl_fifo_valid_li[k])
           ,.ready_param_o(ps_to_pl_fifo_ready_lo[k])
           ,.data_i(S_AXI_WDATA)

           ,.v_o(ps_to_pl_fifo_valid_lo[k])
           ,.data_o(ps_to_pl_fifo_data_lo[k])
           ,.yumi_i(ps_to_pl_fifo_yumi_li[k])
           );

        always @(negedge S_AXI_ACLK)
          begin
             assert(~S_AXI_ARESETN | ~slv_wr_sel_one_hot[num_regs_ps_to_pl_p+k] | ps_to_pl_fifo_ready_lo[k])
               else $error("write to full fifo");
          end

        bsg_flow_counter #(.els_p(4)
                           ,.count_free_p(1)
                           ) bfc
          (.clk_i(S_AXI_ACLK)
           ,.reset_i(~S_AXI_ARESETN)
           ,.v_i    (ps_to_pl_fifo_valid_li[k])
           ,.ready_param_i(ps_to_pl_fifo_ready_lo[k])
           ,.yumi_i (ps_to_pl_fifo_yumi_li [k])
           ,.count_o(ps_to_pl_fifo_ctrs    [k])
           );

     end // block: rof2

   logic [num_fifo_pl_to_ps_p-1:0][C_S_AXI_DATA_WIDTH-1:0] pl_to_ps_fifo_data_r, pl_to_ps_fifo_data_li;
   logic [num_fifo_pl_to_ps_p-1:0]                         pl_to_ps_fifo_valid_lo,
                                                           pl_to_ps_fifo_ready_lo,
                                                           pl_to_ps_fifo_valid_li,
                                                           pl_to_ps_fifo_yumi_li;

   logic [num_fifo_pl_to_ps_p-1:0][`BSG_WIDTH(4)-1:0]      pl_to_ps_fifo_ctrs;
   logic [num_fifo_pl_to_ps_p-1:0][C_S_AXI_DATA_WIDTH-1:0] pl_to_ps_fifo_ctrs_full;

   for (genvar k=0; k < num_fifo_pl_to_ps_p; k++)
     begin: rof4
        assign pl_to_ps_fifo_data_li[k]  = pl_to_ps_fifo_data_i  [k];
        assign pl_to_ps_fifo_valid_li[k] = pl_to_ps_fifo_v_i     [k];
        assign pl_to_ps_fifo_ready_o[k]  = pl_to_ps_fifo_ready_lo[k];
     end

   for (genvar k=0; k < num_fifo_ps_to_pl_p; k++)
     begin: rof1
        assign ps_to_pl_fifo_yumi_li[k] = ps_to_pl_fifo_yumi_i  [k];
        assign ps_to_pl_fifo_data_o[k]  = ps_to_pl_fifo_data_lo [k];
        assign ps_to_pl_fifo_v_o[k]     = ps_to_pl_fifo_valid_lo[k];
     end

   always @(posedge S_AXI_ACLK) begin
     if (S_AXI_ARESETN == 1'b0) begin
       csr_data_new_r <= '0;
     end
     else begin
       csr_data_new_r <= slv_wr_sel_one_hot[num_regs_ps_to_pl_p-1:0];
     end
   end

   for (genvar k=0; k < num_regs_ps_to_pl_p; k++)
     begin: rof5
        assign csr_data_o[k] = slv_r[k];
        assign csr_data_new_o[k] = csr_data_new_r[k];
     end

   // END

   // instantiate out (pl_to_ps) FIFOs

   for (genvar k=0; k < num_fifo_pl_to_ps_p; k++)
     begin: pl_to_ps

        assign pl_to_ps_fifo_ctrs_full[k] = (C_S_AXI_DATA_WIDTH) ' (pl_to_ps_fifo_ctrs[k]);

        assign pl_to_ps_fifo_yumi_li[k] = pl_to_ps_fifo_valid_lo[k] & slv_rd_sel_one_hot[num_regs_ps_to_pl_p+k];

        bsg_fifo_1r1w_small #(.width_p(C_S_AXI_DATA_WIDTH), .els_p(4)) fifo
          (.clk_i(S_AXI_ACLK)
           ,.reset_i(~S_AXI_ARESETN)
           ,.v_i(    pl_to_ps_fifo_valid_li[k])
           ,.ready_param_o(pl_to_ps_fifo_ready_lo[k])

           ,.data_i( pl_to_ps_fifo_data_li [k])

           ,.v_o(   pl_to_ps_fifo_valid_lo[k])
           ,.data_o(pl_to_ps_fifo_data_r  [k])
           // only deque if it is not empty =)
           ,.yumi_i(pl_to_ps_fifo_valid_lo[k] & slv_rd_sel_one_hot[num_regs_ps_to_pl_p+k])
           );

       bsg_flow_counter #(.els_p(4)
                          ,.count_free_p(0)
                          ) bfc
         (.clk_i   (S_AXI_ACLK)
          ,.reset_i(~S_AXI_ARESETN)
          ,.v_i    (pl_to_ps_fifo_valid_li[k])
          ,.ready_param_i(pl_to_ps_fifo_ready_lo[k])
          ,.yumi_i (pl_to_ps_fifo_yumi_li [k])
          ,.count_o(pl_to_ps_fifo_ctrs    [k])
          );

        always @(negedge S_AXI_ACLK)
          begin
             assert(~S_AXI_ARESETN | ~slv_rd_sel_one_hot[num_regs_ps_to_pl_p+k] | pl_to_ps_fifo_valid_lo[k])
               else $error("read from empty fifo");
          end
     end

   // Implement write response logic generation
   // The write response and response valid signals are asserted by the slave
   // when axi_wready, S_AXI_WVALID, axi_wready and S_AXI_WVALID are asserted.
   // This marks the acceptance of address and indicates the status of
   // write transaction.

   always @( posedge S_AXI_ACLK )
     begin
        if ( S_AXI_ARESETN == 1'b0 )
          begin
             axi_bvalid  <= 0;
             axi_bresp   <= 2'b0;
          end
        else
          begin
             if (axi_awready && S_AXI_AWVALID && ~axi_bvalid && axi_wready && S_AXI_WVALID)
               begin
                  // indicates a valid write response is available
                  axi_bvalid <= 1'b1;
                  axi_bresp  <= 2'b0; // 'OKAY' response
               end                   // work error responses in future
             else
               begin
                  if (S_AXI_BREADY && axi_bvalid)
                    //check if bready is asserted while bvalid is high)
                    //(there is a possibility that bready is always asserted high)
                    begin
                       axi_bvalid <= 1'b0;
                    end
               end
          end
     end

   // Implement axi_arready generation
   // axi_arready is asserted for one S_AXI_ACLK clock cycle when
   // S_AXI_ARVALID is asserted. axi_awready is
   // de-asserted when reset (active low) is asserted.
   // The read address is also latched when S_AXI_ARVALID is
   // asserted. axi_araddr is reset to zero on reset assertion.

   always @( posedge S_AXI_ACLK )
     begin
        if ( S_AXI_ARESETN == 1'b0 )
          begin
             axi_arready <= 1'b0;
             axi_araddr  <= 0;
          end
        else
          begin
             if (~axi_arready && S_AXI_ARVALID)
               begin
                  // indicates that the slave has acceped the valid read address
                  axi_arready <= 1'b1;
                  // Read address latching
                  axi_araddr  <= S_AXI_ARADDR;
               end
             else
               begin
                  axi_arready <= 1'b0;
               end
            end
     end

   // Implement axi_arvalid generation
   // axi_rvalid is asserted for one S_AXI_ACLK clock cycle when both
   // S_AXI_ARVALID and axi_arready are asserted. The slave registers
   // data are available on the axi_rdata bus at this instance. The
   // assertion of axi_rvalid marks the validity of read data on the
   // bus and axi_rresp indicates the status of read transaction.axi_rvalid
   // is deasserted on reset (active low). axi_rresp and axi_rdata are
   // cleared to zero on reset (active low).
   always @( posedge S_AXI_ACLK )
     begin
          if ( S_AXI_ARESETN == 1'b0 )
            begin
               axi_rvalid <= 0;
               axi_rresp  <= 0;
            end
          else
            begin
               if (axi_arready && S_AXI_ARVALID && ~axi_rvalid)
                 begin
                    // Valid read data is available at the read data bus
                    axi_rvalid <= 1'b1;
                    axi_rresp  <= 2'b0; // 'OKAY' response
                 end
               else if (axi_rvalid && S_AXI_RREADY)
                 begin
                    // Read data is accepted by the master
                    axi_rvalid <= 1'b0;
                 end
            end
     end

   // Implement memory mapped register select and read logic generation
   // Slave register read enable is asserted when valid address is available
   // and the slave is ready to accept the read address.
   assign slv_reg_rden = axi_arready & S_AXI_ARVALID & ~axi_rvalid;

   // the order of items in the input to this one-hot mux determines the order of the
   // address map for PS reads. the default is user csrs, pl_to_ps fifos, pl_to_ps fifo counters, ps_to_pl fifo counters
   // e.g., for 4 regs, 4 ps_to_pl fifos, and 2 pl_to_ps fifos, the read memory map is essentially
   //
   // 0,4,8,C: registers
   // 10, 14: pl_to_ps fifo heads
   // 18, 1C: pl_to_ps fifo counts
   // 20,24,28,2C: ps_to_pl fifo counts

   initial
     begin: info
        $display("-------------------------------");
        $display("BSG: Zynq Shell PL Read Offsets (%m)");

        for (integer k = 0; k < num_regs_ps_to_pl_p; k++)
          $display("%3h: ps_to_pl register %2d",k << 2, k);

        for (integer k = 0; k < num_fifo_pl_to_ps_p; k++)
          $display("%3h: pl_to_ps fifo #%2d data",(num_regs_ps_to_pl_p << 2)+(k<<2),k);

        for (integer k = 0; k < num_fifo_pl_to_ps_p; k++)
          $display("%3h: pl_to_ps fifo #%2d count",((num_fifo_pl_to_ps_p+num_regs_ps_to_pl_p) << 2)+(k<<2),k);

        for (integer k = 0; k < num_fifo_ps_to_pl_p; k++)
          $display("%3h: ps_to_pl fifo #%2d count",((num_fifo_pl_to_ps_p*2+num_regs_ps_to_pl_p) << 2)+(k<<2),k);

        for (integer k = 0; k < num_regs_pl_to_ps_p; k++)
          $display("%3h: pl_to_ps register %2d"
                   ,((num_fifo_pl_to_ps_p*2+num_regs_ps_to_pl_p+num_fifo_ps_to_pl_p) << 2)+(k<<2),k);

        $display("-------------------------------");
        $display("BSG: Zynq Shell PL Write Offsets (%m)");

        for (integer k = 0; k < num_regs_ps_to_pl_p; k++)
          $display("%3h: ps_to_pl register %2d",k << 2, k);

        for (integer k = 0; k < num_fifo_ps_to_pl_p; k++)
          $display("%3h: ps_to_pl fifo #%2d data",((num_regs_ps_to_pl_p) << 2)+(k<<2),k);
     end

   bsg_mux_one_hot #(.width_p(C_S_AXI_DATA_WIDTH),.els_p(read_locs_lp)) muxoh
     (.data_i({csr_data_i, ps_to_pl_fifo_ctrs_full, pl_to_ps_fifo_ctrs_full, pl_to_ps_fifo_data_r, slv_r})
      ,.sel_one_hot_i(slv_rd_sel_one_hot)
      ,.data_o(reg_data_out)
      );

   // Output register or memory read data
   always @( posedge S_AXI_ACLK )
     begin
        if ( S_AXI_ARESETN == 1'b0 )
          begin
             axi_rdata  <= 0;
          end
          else
            begin
               // When there is a valid read address (S_AXI_ARVALID) with
               // acceptance of read address by the slave (axi_arready),
               // output the read dada
               if (slv_reg_rden)
                 begin
                    axi_rdata <= reg_data_out;     // register read data
                 end
            end
     end

endmodule


// Copyright 2020 ETH Zurich and University of Bologna.
// Solderpad Hardware License, Version 0.51, see LICENSE for details.
// SPDX-License-Identifier: SHL-0.51

module ita_inp2_mux
  import ita_package::*;

    inp2_o = '0;

    if (calc_en_i) begin
      inp2_o = weight_i;
    end
  end

 endmodule

(
  input  logic         clk_i         ,
  input  logic         rst_ni        ,
  input  logic         calc_en_i     ,
  input  weight_t      weight_i  ,


/*******************************************************************************
#   +html+<pre>
#
#   FILENAME: tremolo.sv
#   AUTHOR: Greg Taylor     CREATION DATE: 2 Nov 2014
#
#   DESCRIPTION:
#
#   CHANGE HISTORY:
#   2 Nov 2014    Greg Taylor
#       Initial version
#
#   Copyright (C) 2014 Greg Taylor <gtaylor@sonic.net>
#
#   This file is part of OPL3 FPGA.
#
#   OPL3 FPGA is free software: you can redistribute it and/or modify
#   it under the terms of the GNU Lesser General Public License as published by
#   the Free Software Foundation, either version 3 of the License, or
#   (at your option) any later version.
#
#   OPL3 FPGA 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 Lesser General Public License for more details.
#
#   You should have received a copy of the GNU Lesser General Public License
#   along with OPL3 FPGA.  If not, see <http://www.gnu.org/licenses/>.
#
#   Original Java Code:
#   Copyright (C) 2008 Robson Cozendey <robson@cozendey.com>
#
#   Original C++ Code:
#   Copyright (C) 2012  Steffen Ohrendorf <steffen.ohrendorf@gmx.de>
#
#   Some code based on forum posts in:
#   http://forums.submarine.org.uk/phpBB/viewforum.php?f=9,
#   Copyright (C) 2010-2013 by carbon14 and opl3
#
#******************************************************************************/
`timescale 1ns / 1ps
`default_nettype none // disable implicit net type declarations

module tremolo
    import opl3_pkg::*;

);
    localparam PIPELINE_DELAY = 2;

    logic [TREMOLO_INDEX_WIDTH-1:0] tremolo_index_p1 = '0;
    logic [TREMOLO_INDEX_WIDTH-8-1:0] am_val_tmp0_p1;
    logic [AM_VAL_WIDTH-1:0] am_val_tmp1_p1;
    logic dam_p1 = 0;

    /*
     * Low-Frequency Oscillator (LFO)
     * 3.7 Hz (Sample Freq/2**14)
     */
    always_ff @(posedge clk)
        if (sample_clk_en && bank_num == 0 && op_num == 0)
            if (tremolo_index_p1 == TREMOLO_MAX_COUNT - 1)
                tremolo_index_p1 <= 0;
            else
                tremolo_index_p1 <= tremolo_index_p1 + 1;

    always_comb am_val_tmp0_p1 = tremolo_index_p1 >> 8;

    always_comb
        if (am_val_tmp0_p1 > 26)
            am_val_tmp1_p1 = 2*26 + ~am_val_tmp0_p1;
        else
            am_val_tmp1_p1 = am_val_tmp0_p1;

    always_ff @(posedge clk) begin
        dam_p1 <= dam;

        if (dam_p1)
            am_val_p2 <= am_val_tmp1_p1;
        else
            am_val_p2 <= am_val_tmp1_p1 >> 2;
    end
endmodule
`default_nettype wire

(
    input wire clk,
    input wire sample_clk_en,
    input wire [BANK_NUM_WIDTH-1:0] bank_num,


`ifndef TVIP_AXI_IF_SV
`define TVIP_AXI_IF_SV
interface tvip_axi_if (
  input var aclk,
  input var areset_n
);
  import  tvip_axi_types_pkg::*;

  //  Write Address Channel
  logic                 awvalid;
  logic                 awready;
  tvip_axi_id           awid;
  tvip_axi_address      awaddr;
  tvip_axi_burst_length awlen;
  tvip_axi_burst_size   awsize;
  tvip_axi_burst_type   awburst;
  tvip_axi_write_cache  awcache;
  tvip_axi_protection   awprot;
  tvip_axi_qos          awqos;
  //  Write Data Channel
  logic                 wvalid;
  logic                 wready;
  tvip_axi_data         wdata;
  tvip_axi_strobe       wstrb;
  logic                 wlast;
  //  Write Response Channel
  logic                 bvalid;
  logic                 bready;
  tvip_axi_id           bid;
  tvip_axi_response     bresp;
  //  Read Address Channel
  logic                 arvalid;
  logic                 arready;
  tvip_axi_id           arid;
  tvip_axi_address      araddr;
  tvip_axi_burst_length arlen;
  tvip_axi_burst_size   arsize;
  tvip_axi_burst_type   arburst;
  tvip_axi_read_cache   arcache;
  tvip_axi_protection   arprot;
  tvip_axi_qos          arqos;
  //  Read Data Channel
  logic                 rvalid;
  logic                 rready;
  tvip_axi_id           rid;
  tvip_axi_data         rdata;
  tvip_axi_response     rresp;
  logic                 rlast;

  clocking master_cb @(posedge aclk, negedge areset_n);
    output  awvalid;
    input   awready;
    output  awid;
    output  awaddr;
    output  awlen;
    output  awsize;
    output  awburst;
    output  awprot;
    output  awcache;
    output  awqos;
    output  wvalid;
    input   wready;
    output  wdata;
    output  wstrb;
    output  wlast;
    input   bvalid;
    output  bready;
    input   bid;
    input   bresp;
    output  arvalid;
    input   arready;
    output  arid;
    output  araddr;
    output  arlen;
    output  arsize;
    output  arburst;
    output  arcache;
    output  arprot;
    output  arqos;
    input   rvalid;
    output  rready;
    input   rid;
    input   rdata;
    input   rresp;
    input   rlast;
  endclocking

  clocking slave_cb @(posedge aclk, negedge areset_n);
    input   awvalid;
    output  awready;
    input   awid;
    input   awaddr;
    input   awlen;
    input   awsize;
    input   awburst;
    input   awcache;
    input   awprot;
    input   awqos;
    input   wvalid;
    output  wready;
    input   wdata;
    input   wstrb;
    input   wlast;
    output  bvalid;
    input   bready;
    output  bid;
    output  bresp;
    input   arvalid;
    output  arready;
    input   arid;
    input   araddr;
    input   arlen;
    input   arsize;
    input   arburst;
    input   arcache;
    input   arprot;
    input   arqos;
    output  rvalid;
    input   rready;
    output  rid;
    output  rdata;
    output  rresp;
    output  rlast;
  endclocking

  clocking monitor_cb @(posedge aclk);
    input areset_n;
    input awvalid;
    input awready;
    input awid;
    input awaddr;
    input awlen;
    input awsize;
    input awburst;
    input awcache;
    input awprot;
    input awqos;
    input wvalid;
    input wready;
    input wdata;
    input wstrb;
    input wlast;
    input bvalid;
    input bready;
    input bid;
    input bresp;
    input arvalid;
    input arready;
    input arid;
    input araddr;
    input arlen;
    input arsize;
    input arburst;
    input arcache;
    input arprot;
    input arqos;
    input rvalid;
    input rready;
    input rid;
    input rdata;
    input rresp;
    input rlast;
  endclocking

  event at_master_cb_edge;

  event at_monitor_cb_edge;

  always @(master_cb) begin
    ->at_master_cb_edge;
  end

  always @(slave_cb) begin
    ->at_slave_cb_edge;
  end

  always @(monitor_cb) begin
    ->at_monitor_cb_edge;
  end
endinterface
`endif


/*
  This does a multiplication using programmable width multipliers,
  followed by product accumulation using compressor trees. Next stage then
  does carry propagates.

  Copyright (C) 2019  Benjamin Devlin

  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 <https://www.gnu.org/licenses/>.
 */

module multiplier #(
  parameter DAT_BITS,
  parameter CTL_BITS,
  parameter A_DSP_W = 26,
  parameter B_DSP_W = 17,
  parameter AGRID_W = 32,
  parameter NUM_ACCUM_PIPE = 2
)(
  input i_clk,
  input i_rst,
  if_axi_stream.sink   i_mul,
  if_axi_stream.source o_mul
);

localparam int NUM_COL = (DAT_BITS+A_DSP_W-1)/A_DSP_W;
localparam int NUM_ROW = (DAT_BITS+B_DSP_W-1)/B_DSP_W;
localparam int NUM_GRID = (2*DAT_BITS+AGRID_W-1)/AGRID_W;
localparam int BIT_LEN = A_DSP_W+B_DSP_W+$clog2(NUM_GRID);
localparam int PIPE = 4+NUM_ACCUM_PIPE;
localparam int GRIDS_PER_PIPE = NUM_GRID/NUM_ACCUM_PIPE;

if_axi_stream #(.DAT_BITS(DAT_BITS*2), .CTL_BITS(CTL_BITS)) o_mul_int (i_clk);

logic [A_DSP_W*NUM_COL-1:0] dat_a;
logic [B_DSP_W*NUM_ROW-1:0] dat_b;
(* DONT_TOUCH = "yes" *) logic [A_DSP_W+B_DSP_W-1:0] mul_grid [NUM_COL][NUM_ROW];
logic [(A_DSP_W+B_DSP_W+DAT_BITS*2)-1:0] mul_grid_flat [NUM_COL*NUM_ROW];
logic [NUM_GRID-1:0][BIT_LEN-1:0] accum_out;

logic [NUM_GRID-1:0][BIT_LEN-1:0] accum_grid [NUM_ACCUM_PIPE];
logic [(DAT_BITS*2)-1:0] mul_res [NUM_ACCUM_PIPE];

logic [PIPE-1:0] val, sop, eop;
logic [PIPE-1:0][CTL_BITS-1:0] ctl;

logic adder_rdy_o, adder_val_o, adder_sop_o, adder_eop_o;
logic [CTL_BITS-1:0] adder_ctl_o;

genvar gx, gy;

// Flow control
always_comb begin
  i_mul.rdy = adder_rdy_o;
  o_mul_int.val = val[PIPE-1];
  o_mul_int.sop = sop[PIPE-1];
  o_mul_int.eop = eop[PIPE-1];
  o_mul_int.ctl = ctl[PIPE-1];
  o_mul_int.dat = mul_res[NUM_ACCUM_PIPE-1];
  o_mul_int.err = 0;
  o_mul_int.mod = 0;
end

// Logic for handling multiple pipelines
always_ff @ (posedge i_clk) begin
  if (i_rst) begin
    val <= 0;
    sop <= 0;
    eop <= 0;
    ctl <= 0;
  end else begin
    // Before the pipelined adder
    if (adder_rdy_o) begin
      val[1:0] <= {val[1:0], i_mul.val};
      sop[1:0] <= {sop[1:0], i_mul.sop};
      eop[1:0] <= {eop[1:0], i_mul.eop};
      ctl[1:0] <= {ctl[1:0], i_mul.ctl};
    end

    // After the pipelined adder
    if (o_mul_int.rdy) begin
      val[PIPE-1:2] <= {val[PIPE-1:2], adder_val_o};
      sop[PIPE-1:2] <= {sop[PIPE-1:2], adder_sop_o};
      eop[PIPE-1:2] <= {eop[PIPE-1:2], adder_eop_o};
      ctl[PIPE-1:2] <= {ctl[PIPE-1:2], adder_ctl_o};
    end

  end
end

always_ff @ (posedge i_clk) begin
  if (i_rst) begin
    dat_a <= 0;
    dat_b <= 0;

  end else begin
    if (adder_rdy_o) begin
      dat_a <= 0;
      dat_b <= 0;
      dat_a <= i_mul.dat[0+:DAT_BITS];
      dat_b <= i_mul.dat[DAT_BITS+:DAT_BITS];
    end
  end
end

always_ff @ (posedge i_clk) begin
  for (int i = 0; i < NUM_COL; i++)
    for (int j = 0; j < NUM_ROW; j++) begin
      if (adder_rdy_o)
        mul_grid[i][j] <= dat_a[i*A_DSP_W +: A_DSP_W] * dat_b[j*B_DSP_W +: B_DSP_W];
    end
end

// Convert the multiplication grid into a flat grid
always_comb begin
  for (int i = 0; i < NUM_COL; i++)
    for (int j = 0; j < NUM_ROW; j++) begin
      mul_grid_flat[(i*NUM_ROW)+j] = 0;
      mul_grid_flat[(i*NUM_ROW)+j][(i*A_DSP_W)+(j*B_DSP_W) +: A_DSP_W+B_DSP_W] = mul_grid[i][j];
    end
end

// Accumulate the columns
generate
  for (gx = 0; gx < NUM_GRID; gx++) begin: GEN_ACCUM_GRID

    logic [BIT_LEN-1:0] terms [NUM_COL*NUM_ROW];
    logic adder_rdy, adder_val, adder_sop, adder_eop;
    logic [CTL_BITS-1:0] adder_ctl;

    for (gy = 0; gy < NUM_COL*NUM_ROW; gy++) begin: GEN_ACCUM_VAL

      always_comb begin
        terms[gy] =  mul_grid_flat[gy][gx*AGRID_W +: AGRID_W];
      end
    end

    logic [BIT_LEN-1:0] res;

    pipe_adder_tree_log_n #(
      .NUM_ELEMENTS    ( NUM_COL*NUM_ROW ),
      .BIT_LEN         ( BIT_LEN         ),
      .CTL_BITS        ( CTL_BITS        ),
      .STAGES_PER_PIPE ( 4               ),
      .N               ( 2               )
    )
    pipe_adder_tree_log_n (
      .i_clk   ( i_clk         ),
      .i_rst   ( i_rst         ),
      .i_terms ( terms         ),
      .i_rdy   ( o_mul_int.rdy ),
      .i_ctl   ( ctl[1]        ),
      .i_sop   ( sop[1]        ),
      .i_eop   ( eop[1]        ),
      .o_ctl   ( adder_ctl     ),
      .i_val   ( val[1]        ),
      .o_rdy   ( adder_rdy     ),
      .o_val   ( adder_val     ),
      .o_sop   ( adder_sop     ),
      .o_eop   ( adder_eop     ),
      .o_s     ( res   )
    );

    if (gx == 0) begin
      always_comb begin
        adder_sop_o = adder_sop;
        adder_eop_o = adder_eop;
        adder_val_o = adder_val;
        adder_ctl_o = adder_ctl;
        adder_rdy_o = adder_rdy;
      end
    end

    // Add the result
    always_ff @ (posedge i_clk) begin
      if (o_mul_int.rdy)
        accum_out[gx] <= res;
    end

  end
endgenerate

// This stage propagates the carry
generate
  for (gx = 0; gx < NUM_ACCUM_PIPE; gx++) begin: ACCUM_PIPE_GEN
    logic [DAT_BITS*2-1:0] mul_res_comb;
    always_comb begin
      mul_res_comb = gx > 0 ? mul_res[gx-1][(DAT_BITS*2)-1:gx*GRIDS_PER_PIPE*AGRID_W] : 0;
      for (int i = 0; i < GRIDS_PER_PIPE; i++) begin
        mul_res_comb += accum_grid[gx][i+(GRIDS_PER_PIPE*gx)] << i*AGRID_W;
      end
    end

    always_ff @ (posedge i_clk) begin
      if (o_mul_int.rdy) begin
        accum_grid[gx] <= gx > 0 ? accum_grid[gx-1] : accum_out;
        mul_res[gx] <= gx > 0 ? {mul_res_comb, mul_res[gx-1][gx*GRIDS_PER_PIPE*AGRID_W-1:0]} : mul_res_comb;
      end
    end

  end
endgenerate

// Single pipeline on output
pipeline_bp_if_single #(
  .DAT_BITS  ( DAT_BITS*2 ),

)
pipeline_bp_if_single (
  .i_rst ( i_rst     ),
  .i_clk ( i_clk     ),
  .i_if  ( o_mul_int ),
  .o_if  ( o_mul     )
);
endmodule


`timescale 1 ns / 1 ps
`include "npu.vh"

module npu_tb ();

// Testbench Parameters
localparam CLK_PERIOD = 4;
// NPU Parameters
localparam EW = `EW;
localparam ACCW = `ACCW;
localparam DOTW = `DOTW;
localparam NTILE = `NTILE;
localparam NDPE = `NDPE;
localparam NMFU = `NMFU;
localparam NVRF = `NVRF;
localparam NMRF = `NMRF;
localparam VRFD = `VRFD;
localparam VRFAW = `VRFAW;
localparam MRFD = `MRFD;
localparam MRFAW = `MRFAW;
localparam NSIZE = `NSIZE;
localparam NSIZEW = `NSIZEW;
localparam NTAG = `NTAG;
localparam NTAGW = `NTAGW;
localparam DOT_PER_DSP = `DOT_PER_DSP;
localparam PRIME_DOTW = `PRIME_DOTW;
localparam NUM_DSP = `NUM_DSP;
localparam NUM_ACCUM = `NUM_ACCUM;
localparam ACCIDW = `ACCIDW;
localparam VRFIDW = `VRFIDW;
localparam MRFIDW = `MRFIDW;
localparam MIW_MVU = `MIW_MVU;
localparam UIW_MVU = `UIW_MVU;
localparam MIW_EVRF = `MIW_EVRF;
localparam UIW_EVRF = `UIW_EVRF;
localparam MIW_MFU = `MIW_MFU;
localparam UIW_MFU = `UIW_MFU;
localparam MIW_LD = `MIW_LD;
localparam UIW_LD = `UIW_LD;
localparam MICW = `MICW;
localparam QDEPTH = `QDEPTH;
localparam WB_LMT = `WB_LMT;
localparam WB_LMTW = `WB_LMTW;
localparam MULT_LATENCY = `MULT_LATENCY;
localparam DPE_PIPELINE = `DPE_PIPELINE;
localparam SIM_FLAG = `SIM_FLAG;
localparam TILES_THRESHOLD = `TILES_THRESHOLD;
localparam DPES_THRESHOLD = `DPES_THRESHOLD;
localparam RTL_DIR = `RTL_DIR;
localparam TARGET_FPGA = `TARGET_FPGA;
localparam FILLED_MRFD = `FILLED_MRFD;
localparam OUTPUT_BUFFER_SIZE = `OUTPUT_BUFFER_SIZE;
localparam INST_DEPTH = `INST_DEPTH;
localparam INST_ADDRW = `INST_ADDRW;

logic clk;
logic rst;
// Signals for MRF load
logic [MRFAW-1:0] mrf_wr_addr;
logic [EW*DOTW-1:0] mrf_wr_data;
logic mrf_wr_en;
logic [MRFIDW-1:0] mrf_wr_id;
// Signals for instructions load
logic inst_wr_en;
logic inst_wr_rdy;
logic [INST_ADDRW-1:0] inst_wr_addr;
logic [MICW-1:0] inst_wr_data;
// Signals for input write
logic input_wr_en;
logic input_wr_rdy0, input_wr_rdy1;
logic [EW*DOTW-1:0] input_wr_data;
// Signals for output read
logic output_rd_en0, output_rd_en1;
logic output_rd_rdy0, output_rd_rdy1;
logic [EW*DOTW-1:0] output_rd_data0, output_rd_data1;
// Signals for control
logic [2:0] mode;
logic start;
logic [31:0] pc_offset;
logic done;

// Generate a 250MHz clock
initial clk = 1'b1;
always #(CLK_PERIOD/2) clk = ~clk;

// Instantiate the design under test
npu dut(
    .clk					(clk),
    .rst					(rst),

    .i_mrf_wr_addr			(mrf_wr_addr),
    .i_mrf_wr_data			(mrf_wr_data),
    .i_mrf_wr_en			(mrf_wr_id),

    .i_minst_chain_wr_en	(inst_wr_en),
    .i_minst_chain_wr_addr	(inst_wr_addr),
    .i_minst_chain_wr_din	(inst_wr_data),

    .i_ld_in_wr_en 			(input_wr_en),
    .o_ld_in_wr_rdy 		(input_wr_rdy0),
    .i_ld_in_wr_din 		(input_wr_data),
    .i_ld_in_wr_en1			(input_wr_en),
    .o_ld_in_wr_rdy1 		(input_wr_rdy1),
    .i_ld_in_wr_din1 		(input_wr_data),

    .i_ld_out_rd_en 		(output_rd_en0),
    .o_ld_out_rd_rdy 		(output_rd_rdy0),
    .o_ld_out_rd_dout 		(output_rd_data0),
    .i_ld_out_rd_en1 		(output_rd_en1),
    .o_ld_out_rd_rdy1		(output_rd_rdy1),
    .o_ld_out_rd_dout1 		(output_rd_data1),

    .diag_mode 				(mode),
    .i_start 				(start),
    .pc_start_offset 		(pc_offset),
    .o_done 				(done)
);

integer temp;
logic [8000:0] line;
integer tile_id, dpe_id, lane_id, mrf_file_id, mrf_id, mrf_line;
integer mrf_file [0:NUM_DSP-1];
string mrf_hundreds, mrf_tens, mrf_units, mrf_bank;

integer input_file, num_inputs, input_id, input_addr;
integer inst_file, num_inst, inst_id, inst_addr;
integer output_file, num_output, output_id, output_addr;
logic [EW*DOTW-1:0] golden_output;
integer cycles;
logic success;

integer init_done, mrf_done, input_done, inst_done, sim_done;

initial begin
	rst = 1;
	start = 0;
	pc_offset = 0;
	mode = 4;
	mrf_wr_id = 0;
	#(5*CLK_PERIOD);
	rst = 0;
	#(30*CLK_PERIOD);
	init_done = $fopen("init_done", "w");
	$fwrite(init_done, "init done\n");
	$fclose(init_done);

	// Step 1: Load MRF values
	/*mrf_wr_id = 0;
	for (tile_id = 0; tile_id < NTILE; tile_id = tile_id + 1) begin
		for (dpe_id = 0; dpe_id < NDPE; dpe_id = dpe_id + 1) begin
			mrf_id = (tile_id * NDPE) + dpe_id;
			$display("Loading MRF %d", mrf_id);
			for (mrf_file_id = 0; mrf_file_id < NUM_DSP; mrf_file_id = mrf_file_id + 1) begin
				// Open the MIFs for a specific MRF
				mrf_hundreds.itoa(mrf_id / 100);
				mrf_tens.itoa((mrf_id % 100) / 10);
				mrf_units.itoa(mrf_id % 10);
				mrf_bank.itoa(mrf_file_id);
				mrf_file[mrf_file_id] = $fopen({"mif_files/mvu-mrf", mrf_hundreds, mrf_tens, mrf_units, "_", mrf_bank, ".mif"}, "r");

				// Read and ignore the MIF header
				temp = $fgets(line, mrf_file[mrf_file_id]);
				temp = $fgets(line, mrf_file[mrf_file_id]);
				temp = $fgets(line, mrf_file[mrf_file_id]);
				temp = $fgets(line, mrf_file[mrf_file_id]);
				temp = $fgets(line, mrf_file[mrf_file_id]);
				temp = $fgets(line, mrf_file[mrf_file_id]);
			end

			// Read the MRF address and data
			for (mrf_line = 0; mrf_line < FILLED_MRFD; mrf_line = mrf_line + 1) begin
				for (mrf_file_id = 0; mrf_file_id < NUM_DSP; mrf_file_id = mrf_file_id + 1) begin
					temp = $fscanf(mrf_file[mrf_file_id], "%d: %b;\n", mrf_wr_addr, mrf_wr_data[(EW*DOTW)-((mrf_file_id+1)*EW*PRIME_DOTW) +: EW*PRIME_DOTW]);
				end
				mrf_wr_id = (tile_id * NDPE) + dpe_id + 1;
				#(CLK_PERIOD);
			end
		end
	end
	$display("Contents of %d MRFs were loaded successfully!", mrf_id+1);
	mrf_wr_id = 0;
	mrf_done = $fopen("mrf_done", "w");
	$fwrite(mrf_done, "MRF loaded\n");
	$fclose(mrf_done);
	#(CLK_PERIOD);*/

	// Step 2: Load Inputs
	input_file = $fopen({"mif_files/input.mif"}, "r");
	temp = $fscanf(input_file, "DEPTH = %d;\n", num_inputs);
	temp = $fgets(line, input_file);
	temp = $fgets(line, input_file);
	temp = $fgets(line, input_file);
	temp = $fgets(line, input_file);
	temp = $fgets(line, input_file);
	input_id = 0;
	// Feed in input vectors as long as the NPU is ready to accept new inputs
	while(input_id < num_inputs) begin
		if(input_wr_rdy0 && input_wr_rdy1) begin
			temp = $fscanf(input_file, "%d: %x;\n", input_addr, input_wr_data);
			input_wr_en = 1;
			#(CLK_PERIOD);
			input_id = input_id + 1;
		end else begin
			input_wr_en = 0;
			#(CLK_PERIOD);
		end
	end
	$display("%d input vectors were loaded successfully!", input_id);
	input_wr_en = 0;
	input_done = $fopen("input_done", "w");
	$fwrite(input_done, "Inputs loaded\n");
	$fclose(input_done);
	#(CLK_PERIOD);

	// Step 3: Load Instructions
	inst_file = $fopen({"mif_files/top_sched.mif"}, "r");
	temp = $fscanf(inst_file, "DEPTH = %d;\n", num_inst);
	temp = $fgets(line, inst_file);
	temp = $fgets(line, inst_file);
	temp = $fgets(line, inst_file);
	temp = $fgets(line, inst_file);
	temp = $fgets(line, inst_file);
	inst_id = 0;
	// Feed in instructions as long as the NPU is ready to accept new inputs
	while(inst_id < num_inst) begin
		temp = $fscanf(inst_file, "%d: %b;\n", inst_wr_addr, inst_wr_data);
		$display("%d: %b", inst_wr_addr, inst_wr_data);
		inst_wr_en = 1;
		#(CLK_PERIOD);
		inst_id = inst_id + 1;
	end
	$display("%d instructions were loaded successfully!", inst_id);
	inst_wr_en = 0;
	inst_done = $fopen("inst_done", "w");
	$fwrite(inst_done, "Instructions loaded\n");
	$fclose(inst_done);
	#(CLK_PERIOD);

	// Step 4: Prepare for simulation
	output_file = $fopen({"mif_files/output.mif"}, "r");
	temp = $fscanf(output_file, "DEPTH = %d;\n", num_output);
	temp = $fgets(line, output_file);
	temp = $fgets(line, output_file);
	temp = $fgets(line, output_file);
	temp = $fgets(line, output_file);
	temp = $fgets(line, output_file);
	output_id = 0;
	cycles = 0;
	pc_offset = 0;
	mode = 4;
	success = 1;
	#(CLK_PERIOD);

	// Step 5: Run the simulation and listen for outputs
	start = 1;
	#(CLK_PERIOD);
	cycles = cycles + 1;

	while (output_id < num_output) begin
		if(output_rd_rdy0 && output_rd_rdy1) begin
			temp = $fscanf(output_file, "%d: %x;\n", output_addr, golden_output);
			if((golden_output == output_rd_data0) && (golden_output == output_rd_data1)) begin
				success = success & 1'b1;
				$display("Output %d is matching", output_id);
				//$display("Expected: %x", golden_output);
				//$display("Got: %x", output_rd_data0);
			end else begin
				success = success & 1'b0;
				$display("Output %d is not matching", output_id);
				$display("Expected: %x", golden_output);
				$display("Got: %x", output_rd_data0);
			end
			output_rd_en0 = 1;
			output_rd_en1 = 1;
			output_id = output_id + 1;
		end else begin
			output_rd_en0 = 0;
			output_rd_en1 = 0;
		end
		#(CLK_PERIOD);
		cycles = cycles + 1;
	end

	sim_done = $fopen("sim_done", "w");
	if (success)begin
		$display("****************************************************************************************");
		$display("SUCCESS! ALL OUTPUTS ARE MATCHING! Runtime = %d cycle(s)", cycles);
		$display("****************************************************************************************");
		$fwrite(sim_done, "PASS\n");
	end else begin
		$display("****************************************************************************************");
		$display("FAILURE! SOME OUTPUTS ARE NOT MATCHING! Runtime = %d cycle(s)", cycles);
		$display("****************************************************************************************");
		$fwrite(sim_done, "FAIL\n");
	end
	$fwrite(sim_done, "%0d", cycles);
	$fclose(sim_done);

	//$stop(0);
	$finish;
end

endmodule