So I'd like to use Arlet's 6502 in a design, but I'm having difficulties getting it to integrate with the blockram model I'm using. If I put byte 0xa0 (ASL A) into memory at the location pointed to by PC ($0000), set the interrupt vector to be (PC-1) and let the clock run for a bit, I see the below:
It looks as though the CPU is running through its BRK routine, fetching the vector, jumping through it, and entering DECODE state, but the result coming back from RAM is only available on the following clock cycle, so IR is invalid, and we end up going through another BRK instead of successfully decoding the 'ASL A'.
The
README on his git repository states
Quote:
Note: the 6502 core assumes a synchronous memory. This means that valid data (DI) is expected on the cycle *after* valid address. This allows direct connection to (Xilinx) block RAMs
... which seems to be what's happening - Address 0000 (PC) is presented 6 clocks after the 'reset' line is toggled low (shown by the marker), and the data for the instruction at that address appears on the next clock.
The CPU is linked through to the blockram just as you might expect:
Code:
module top
(
output [15:0] memAB,
output [7:0] memDO,
input clk,
input rst_n
);
////////////////////////////////////////////////////////////////////////////
// Instantiate the 64k of BlockRAM for main memory
////////////////////////////////////////////////////////////////////////////
wire memWE; // Write-enable
wire [7:0] memDI; // Data read from memory
ram mem_inst
(
.clk(clk),
.addr(memAB),
.we(memWE),
.di(memDI),
.do0(memDO)
);
////////////////////////////////////////////////////////////////////////////
// Instantiate the CPU
////////////////////////////////////////////////////////////////////////////
reg irq_n; // External in: IRQ
reg nmi_n; // External in: Non-maskable IRQ
cpu cpu_inst
(
.clk(clk), // System clock
.reset(~rst_n), // Active high reset
.DI(memDO), // Read: data @ (address)
.DO(memDI), // Write)
.AB(memAB), // Address bus
.WE(memWE), // Write-enable
.RDY(1'b1), // Pause the CPU if low
.IRQ(irq_n), // external IRQ if low
.NMI(nmi_n) // non-maskable IRQ if low
);
endmodule
And the blockram is a standard dual-ported inferred model that returns the data on the next clock:
Code:
module bram
#(
parameter DATA = 8,
parameter ADDR = 16
)
(
// Port A
input wire clka,
input wire wea,
input wire [ADDR-1:0] addra,
input wire [DATA-1:0] dina,
output reg [DATA-1:0] douta,
// Port B
input wire clkb,
input wire web,
input wire [ADDR-1:0] addrb,
input wire [DATA-1:0] dinb,
output reg [DATA-1:0] doutb
);
// Shared memory
reg [DATA-1:0] mem [(2**ADDR)-1:0];
// Port A
always @(posedge clka)
begin
if (wea)
mem[addra] <= dina;
else
douta <= mem[addra];
end
// Port B
always @(posedge clkb)
begin
if (web)
mem[addrb] <= dinb;
else
doutb <= mem[addrb];
end
endmodule
(There's an intermediate module called 'ram', but that is transparent for this discussion - it's a straight wire-through). I'm sure Arlet's design works well for others, so I was wondering if it's obvious what I'm doing wrong ?
[Edit: Hmm, I noticed that BRK is supposed to be 7 cycles, not 6. Perhaps it's a timing thing... I'm also a little concerned that 'S' is going to 'xx' as a consequence of state BRK3 writing to the register...]
Cheers
Simon