My new verilog 65C02 core.
Re: My new verilog 65C02 core.
Excellent bug! Can't take interrupts in decimal mode...
Re: My new verilog 65C02 core.
I know people always wanted decimal interrupts and resets, so I have provided the opportunity here
You can modify the microcode to have different behavior.
Re: My new verilog 65C02 core.
Quite an interesting idea to make the D flag swap between two instruction sets, or two interrupt regimes, or something...
Re: My new verilog 65C02 core.
On a serious note, this little side quest did lead me to a new idea of using the feature of the block RAMs to provide a initial value on the output registers when the FPGA is configured, so it will automatically jump to the reset handler, instead of starting with a BRK.
In addition, the block RAMs have a reset input that can be configured to load a different constant value in the output registers. If I tie this input to the reset input of the CPU, I don't need any logic to handle the resets.
In addition, the block RAMs have a reset input that can be configured to load a different constant value in the output registers. If I tie this input to the reset input of the CPU, I don't need any logic to handle the resets.
Re: My new verilog 65C02 core.
BigEd wrote:
Quite an interesting idea to make the D flag swap between two instruction sets, or two interrupt regimes, or something...
Of course, if you're really interested in such a thing, it's easy enough to use 2 ROMs.
Re: My new verilog 65C02 core.
Arlet wrote:
In addition, the block RAMs have a reset input that can be configured to load a different constant value in the output registers. If I tie this input to the reset input of the CPU, I don't need any logic to handle the resets.
Re: My new verilog 65C02 core.
Using the INIT/SRVAL parameters of the block RAM, plus combining the NMI and IRQ in a single select, I managed to get the input mux to the microcode address reduced from 6 choices to 4, which narrows the select signal from 3 bits to 2.
Here's the 9 bit address into the microcode ROM. In the first cycle, we take the opcode as index. After that, each control word contains a pointer to next address. The 'D' bit is set according to decimal flag. When the addressing mode is done, and the effective address is available, the 'finish' handler is called. This allows common operations to be combined with the same microcode. The 'finish' address is set in the first microcode instruction. When an interrupt is taken, the D bit is replaced by bit indicating NMI or IRQ.
Here's the 9 bit address into the microcode ROM. In the first cycle, we take the opcode as index. After that, each control word contains a pointer to next address. The 'D' bit is set according to decimal flag. When the addressing mode is done, and the effective address is available, the 'finish' handler is called. This allows common operations to be combined with the same microcode. The 'finish' address is set in the first microcode instruction. When an interrupt is taken, the D bit is replaced by bit indicating NMI or IRQ.
Code: Select all
8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+
| 0 | opcode (DB) | opcode lookup
+---+---+---+---+---+---+---+---+---+
| 1 | D | jmp next | next instruction
+---+---+---+---+---+---+---+---+---+
| 1 | D | 1 0 | finish | finish handler
+---+---+---+---+---+---+---+---+---+
| 1 |N/I| 1 1 0 0 0 0 0 | IRQ/NMI handler @160/@1E0
+---+---+---+---+---+---+---+---+---+
Re: My new verilog 65C02 core.
I have a test board with a couple of 7 segment displays, driven by a chain of 74HC595s. To access the chain, the CPU can just write a byte to the peripheral port. The problem is that the chain is a bit slow (I bought the wrong devices), and the CPU needs to wait a bit. So I figured this would be a good time to test out the RDY signal in practice, and added a bit of logic to stop the CPU if the SPI was still busy. Works like a charm. Not so good for the interrupt latency, though 
Re: My new verilog 65C02 core.
I've spent most of the time playing with FPGA hardware. Made a new UART driver, and added timer interrupt. I now have a simple interrupt routine that increments a counter @ 100Hz, and displays it on the 7 segment displays on the board.
But then I noticed that the timer would stop when I entered a command through the UART. It turned out that the JMP (IND) was the problem, because it shares the last couple of cycles with the BRK instruction, including setting the I flag.
I'm surprised that Klaus' test suite didn't catch that.
Microcode is a bit of a double edged sword. It's really flexible and easy to make local adjustments, but it's also much easier to get these subtle bugs that only appear in specific situations. I guess it also doesn't help that my input file is an unreadable wall of binary
But then I noticed that the timer would stop when I entered a command through the UART. It turned out that the JMP (IND) was the problem, because it shares the last couple of cycles with the BRK instruction, including setting the I flag.
I'm surprised that Klaus' test suite didn't catch that.
Microcode is a bit of a double edged sword. It's really flexible and easy to make local adjustments, but it's also much easier to get these subtle bugs that only appear in specific situations. I guess it also doesn't help that my input file is an unreadable wall of binary
Re: My new verilog 65C02 core.
Interesting - not the first test escape!
Re: My new verilog 65C02 core.
I've started on a tool to translate the microcode binary file to human readable text, so that it's easier to check if it's doing the right things. Here's a sample of the first version's output. It shows all opcodes that update a register in their first cycle.
Maybe later I will also write a tool that goes the other way, and reconstruct a binary file from readable text.
Maybe later I will also write a tool that goes the other way, and reconstruct a binary file from readable text.
Code: Select all
00: S = S - 1
08: S = S - 1
0A: A = ASL A
1A: A = A + 1
20: S = S - 1
28: S = S + 1
2A: A = ROL A
3A: A = A - 1
40: S = S + 1
48: S = S - 1
4A: A = LSR A
5A: S = S - 1
60: S = S + 1
68: S = S + 1
6A: A = ROR A
7A: S = S + 1
88: Y = Y - 1
8A: A = X
98: A = Y
9A: S = X
A8: Y = A
AA: X = A
BA: X = S
C8: Y = Y + 1
CA: X = X - 1
DA: S = S - 1
E8: X = X + 1
FA: S = S + 1
Re: My new verilog 65C02 core.
Flags updates are still missing, and the layout needs to be improved, but here's an example of the BRK instruction sequence decoded.
The '<=' operator works as in Verilog, indicating that the result is stored in registers at the next clock edge. The '=' operator happens right away, so that's why you see the stack address being loaded in AB one cycle before the corresponding DO value is set.
Code: Select all
00: [9] AB<={01,S} AH<=DB PC<=AB+1| M<=DB S<=S - 1
[8] AB<={01,S} | DO=PCH M<=DB S<=S - 1
[8] AB<={01,S} | DO=PCL M<=DB S<=S - 1
[f] AB<=BRK | DO=P M<=DB
[c] AB<=AB+1 AH<=DB | M<=DB
[2] AB<={DB,AH} AH<=DB PC<=AB+1| M<=DB
[c] AB<=AB+1 AH<=DB | M<=DB
Re: My new verilog 65C02 core.
Full list for all opcodes on GitHub. Still missing everything related to flags, but that will be added soon.
Better version using markdown
Better version using markdown
Re: My new verilog 65C02 core.
I've added all instruction names to the microcode table and also sorted it by instruction/addressing mode.
Re: My new verilog 65C02 core.
Here's a simplified schematic block diagram of the core datapath.
The blocks with the little '>' in the corner are registers. The output data on the right lines up with the input data on the left in the next cycle, except for the ABL/ABH on the right go into the memory, and then the DB on the left comes out.
The blocks with the little '>' in the corner are registers. The output data on the right lines up with the input data on the left in the next cycle, except for the ABL/ABH on the right go into the memory, and then the DB on the left comes out.