emu-russia / dmgcpu

DMG CPU Reverse Engineering
Creative Commons Zero v1.0 Universal
29 stars 4 forks source link

CPU never writes to external bus #239

Closed Rodrigodd closed 7 months ago

Rodrigodd commented 7 months ago

In #238 I noticed that during instructions that should write to memory, it doesn't output a value to the external bus, it was kept in high impedance. Below is the summary of my investigation, as contained in investigating.md in the linked PR.


LD (HL), A

Data bus stays in high impedance

DataLatch is the only one connected to the external data bus D. Although it is called a "latch" it does not hold any data, it just forwards data from other data buses; that is Res (the result of the ALU) and DL (the internal databus).

But datalatch.md states that it can only output values to DL, and load from either D or Res. But this should not be the case, because we need to output to the external bus, and DataLatch is the only one connected to it.

Also, it specifies DL bus as inout, but never assign z to it. In fact, during the write (WR high) DataBridge also writes to DL, making DL go to invalid state x. Actually, a lot of things tries to write to DL.

(Strangely, DataBridge only writes 0 or z bits to DL. Maybe DL has a pull-up somewhere? Oh, maybe that is what BusPrecharge does, not sure if verilog will simulate that. Oh, this is handle by the use of BusKeeper.)

The value of register A is stored in ReqA/r1q, inverted. This value can be put in the abus or bbus (and Aout, but just affect ALU logic), but during write neither of them are used, s2_op_alu8 (w[3]) and s3_oe_areg_to_rbus (x[35]) are both 0.

image

In screenshot, the execution of the instruction LD (HL), A (77).

ogamespec commented 7 months ago

I am taking this issue into processing as it looks more critical. There are inaccuracies in the description of datalatch on the wiki, it will be corrected now. In fact it contains 2 DLatch (dynamic, using FET gates), acting as transparent latch = BusKeepers. The datalatch also contains a precharge for the internal data bus (DL).

ogamespec commented 7 months ago

Updated approximation of DataLatch schematic for Logisim (#244 )

image

ogamespec commented 7 months ago

I've localized the cause of the conflict:

image

It turns out that the internal bus (DL) is driven by two circuits at once - DataLatch and DataBridge. DataLatch places a transparent latch value on the bus, and DataBridge places a "DV" value (ALU Operand2) on the bus.

That is, there is no solution to the "ground wins" conflict.

The solution I see is to combine 2 DataLatch+DataBridge modules into one to correctly multiplex the buses inside.

Rodrigodd commented 7 months ago

@ogamespec Can you use Verilog assignment strength here? I am not very familiar with it, but I see it being used in dmg-sim (and that Verilator does not support all strength levels).

ogamespec commented 7 months ago

Can you use Verilog assignment strength here?

I doubt that I will set the signal strength correctly, because it is more related to the analog domain, and I don't specialize in analog electronics at all.

ogamespec commented 7 months ago

Merged DataLatch & DataBridge into single instance: #246

It's gotten better, but it's still not great:

image

Instead of writing A -> DV bus -> 0xBC, for some reason 0 is written to the DV bus.

ogamespec commented 7 months ago

Actually, I think that I have fixed this issue and the problem is already in the register block (connections between registers and buses). It is better to move to #240 and check the correctness of the whole register block within that issue.

@Rodrigodd Please see if you have any questions left on this topic?

For DataMux I will also draw a combined transistor circuit with explanations of what and how it connects (but this is for the wiki).

ogamespec commented 7 months ago

I did a comprehensive analysis of the connection of the internal buses to the external data bus and put together a visual schematic of DataLatch+DataBridge, which is now called DataMux (databus multiplexer):

image

The interaction mechanism of bidirectional buses in SM83 is described in this section: https://github.com/emu-russia/dmgcpu/blob/main/buses.md#bidirectional-bus-multiplexing

Rodrigodd commented 7 months ago

I believe the write is now blocked from the other problem I noticed:

The value of register A is stored in ReqA/r1q, inverted. This value can be put in the abus or bbus (and Aout, but just affect ALU logic), but during write neither of them are used, s2_op_alu8 (w[3]) and s3_oe_areg_to_rbus (x[35]) are both 0.

Which may come from the decoder.

But this specific issue may be fixed. I will test later if any other instruction can now write to memory, and how extensive is the decoding problem.

ogamespec commented 7 months ago

Okay, then I'll close this Issue and switch to regblock fixes.

ogamespec commented 7 months ago

@Rodrigodd Fixed, edits online.

Rodrigodd commented 7 months ago

Yep, tested it on dmg-sim and it can now a CALL and RET pair works. The next problem appear to be it taking the wrong branch in a conditional return. Will open a issue/PR once I investigated it more. Thanks!