6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Sat Nov 16, 2024 6:43 pm

All times are UTC




Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next
Author Message
PostPosted: Thu Mar 22, 2018 8:35 pm 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
What happens whilst the Reset pin is being held low?

I know what happens when the pin is released after being held low, the chip goes through its normal and well documented reset process, but what does it do before it is released?

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 22, 2018 9:13 pm 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
The data bus is held in High-Z mode. That's pretty much the most important thing.

The address bus stays online and doesn't change between clock pulses while RESET is active.

You have to keep RESET active for at least 2 clocks or it doesn't work correctly. I don't know exactly why this is the case, but I suspect that it's necessary because the reset sequence is basically handled like a pretend-interrupt. So it finishes the current instruction to get the instruction decoder to state T0.

You probably already know that after RESET goes inactive (high) again, the 6502 reads one more instruction, then pretends to push the return address and flags onto the stack but holds R/!W high to keep that from happening, and then it loads the address from $FFFC/$FFFD and starts executing at the address that's stored there.

Here's a page I wrote a couple of years ago on my old Propeddle project website that shows the address bus and databus during and after a reset: http://propeddle.com/?p=162 (I've abandoned that project and am now working on a simplified version called L-Star, see http://l-star.org).

Attachment:
firstpasmrun.png
firstpasmrun.png [ 20.33 KiB | Viewed 6631 times ]


===Jac


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 22, 2018 10:05 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10981
Location: England
I think I've seen in visual6502 that what happens in the first few cycles of holding reset active will depend on which instruction is being executed. For example here we see JSR producing a stack address but no write occurs. There's no second stack access. The PC gets a bogus value, and reads are made from that bogus address. (Could be interesting if there was a side-effect.)
http://www.visual6502.org/JSSim/expert. ... 8&steps=40


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 22, 2018 10:44 pm 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
The reason I ask is because I'm designing a system where the 6502 CPU is a "slave" to another processor with some shared RAM between the two. To bootstrap the slave, the host can write a small bootstrap program into the shared RAM whilst the slave is being held in reset. Once its done that, it releases the slave which then bootstraps itself using the program & vector left for it by the host. The problem is that the dual-port RAM would perma-block the host if the slave is attempting to "write" to the shared RAM due to some weird consequence of being held in reset. It sounds like this won't be an issue due to R/W being held in the read state.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 22, 2018 10:47 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10981
Location: England
Yes, sounds safe.


Top
 Profile  
Reply with quote  
PostPosted: Thu Mar 22, 2018 11:26 pm 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
On a related note, ~ML is breaking my brain.

Obviously this a multiprocessor system with shared RAM, so I need to use ML to protect the atomicity of RMW instructions (or ban their use on shared RAM); which is easy enough, I just connect ~ML into the ~BUSY signal going to the other CPU, which causes it to wait until ~ML is inactive. The problem is what happens if both Slave and Master happen to assert ~ML at the same time? They'd both be halted and never recover.

I guess I need a circuit or system of some kind whereby ~ML works as described above, EXCEPT if they're both asserted in the same cycle, in which case one is given priority and the relevant CPU allowed to run to completion whilst the other is made to wait (e.g. using RDY). Ideally ~ML blocking should only happen whilst accessing shared RAM, for which there is a chip enable signal called ~SRCE.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 12:13 am 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
Alarm Siren wrote:
The problem is what happens if both Slave and Master happen to assert ~ML at the same time? They'd both be halted and never recover.


If you let the two CPU's run on opposite sides of a 2-phase clock, you'll avoid this problem, and then some. The 6502 doesn't use the data bus during the first half of its clock cycle so another CPU (6502 or something else) could use the data bus during that time. This is used in many, many designs; for example Commodore had two 6502's in their disk drives that worked on opposite phases of the same clock, and most memory-mapped video I've seen is based on letting the 6502 access the video memory during the second half of the system clock whereas the video circuitry reads the video memory during the first half.

If the CPUs run on opposite phases of the same clock, the first CPU that needs to do an atomic operation halts the second one, and there's no chance of a race condition where they both need to do an atomic operation at the same time because the start of an atomic operation on CPU A is always at least half a cycle away from any atomic operation on CPU B and vice versa. And you don't need dual-port RAM either, just multiplexers to select the address on the RAM between address bus A and address bus B.

===Jac


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 12:17 am 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
Unfortunately there will be more than one slave CPU, so this approach won't work. That is also why I need the dual-port RAM, so that the slaves do not interfere with eachother.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 12:37 am 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
Alarm Siren wrote:
Unfortunately there will be more than one slave CPU, so this approach won't work. That is also why I need the dual-port RAM, so that the slaves do not interfere with eachother.


The idea is not restrained to 2 CPUs. As long as only one CPU at a time has a clock that's in the HIGH state, you're good.

===Jac


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 12:43 am 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
jac_goudsmit wrote:
The idea is not restrained to 2 CPUs. As long as only one CPU at a time has a clock that's in the HIGH state, you're good.

Good point, though it would somewhat defeat the point of having multiple cores (or at least, more than two), since they'd effectively be operating in a round-robin fashion rather than truly in parallel.

Also, I've thought about it a little more, and the high/low clock solution doesn't actually solve the underlying issue with RMW instructions (though I do agree is removes the need for dual-port RAM)....
Example:
CPU A enters an INC RMW cycle, reads out a '5' from memory.
CPU B, on the low half of the cycle, starts an INC RMW cycle, reads '5' from the same memory.
CPU A, writes back '5'+1 = '6'.
CPU B, writes back '5'+1 = '6'.
We now have '6' in the memory when we ought to have had '7'. Had ML been used to block CPU B, the answer would have been the correct '7'.

---

The circuit pictured in the attachment almost does what I need...

When the Slave attempts to access the shared RAM whilst the Master's ML is asserted then the BUSY signal to the slave is asserted, stopping it in its tracks until the Master releases its ML. The 7475 flip-flop ensures once one of the two BUSY signals are asserted, the other one cannot be asserted until both have been deasserted, since there's never a reason to block both simultaniously: this ensures forward progress is always made

All of the above is true of the reverse relationship as well.

There are two problems with this circuit: if the Master and Slave both assert their MLs or CEs in the same cycle with either or both of the other signals also asserted, then both BUSY outputs might be asserted, halting all forward progress. The other problem is that the circuit unecessarily stops the Slave when the Master's ML is asserted but it is not accessing the shared RAM (i.e. Master CE is not asserted), and vice-versa.

EDIT: ... and I just thought of another complication. Say Slave CE and Master ML are asserted, so the slave gets halted by BUSY. Now the Master continues and happens to access the same memory address that the Slave was in the middle of writing - because BUSY is hooked up to the 6502's RDY input, the address bus etc on the dual-port RAM is still on the same address. This causes the dual-port RAM to report a contention, which causes it to assert the Master's BUSY signal. Now both BUSYs are asserted and no forward progress is possible. Though actually this may not be possible since ML is asserted first during a read, so maybe the blocked side would always block on a read, which would be OK? .... maybe it would just be easier to ban RMW instructions on the shared RAM, this is getting complicated.


Attachments:
File comment: Incomplete example circuit
Circuit.png
Circuit.png [ 5.75 KiB | Viewed 6607 times ]

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.
Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 2:47 am 
Offline
User avatar

Joined: Thu Jun 23, 2011 2:12 am
Posts: 229
Location: Rancho Cucamonga, California
Alarm Siren wrote:
Good point, though it would somewhat defeat the point of having multiple cores (or at least, more than two), since they'd effectively be operating in a round-robin fashion rather than truly in parallel.


The clock signal for each processor would be increasingly asymmetric, so yes there's some concern about that. Each phase of the clock has to be at least 35ns (according to the WDC datasheet). But that means that with 3 65C02S CPUs you can still run your system at almost 10MHz with one data bus and a 3-phase clock.

Quote:
Also, I've thought about it a little more, and the high/low clock solution doesn't actually solve the underlying issue with RMW instructions (though I do agree is removes the need for dual-port RAM)....


Simply halting the other processors when one processor is doing any RMW instruction would be terribly inefficient.

The RMW output of the WDC 65C02S can help, because it can tell the system when it's accessing a location that it's going to modify and write back. But any circuitry should also take the address into account: if CPU A is executing an INC absolute of a location, any other CPU should ONLY be halted if it tries to access the SAME address. Accessing any other address is fine. But that would be a complicated circuit.

I probably would design a much simpler circuit than one that compares the addresses of all CPU's and makes decisions on halting them.

I thought about it for a while and I'd probably do something like this: Let's say you have an up/down counter (e.g. 74HC193) and an address decoder.
  • Upon system reset, the counter is set to 1111 binary (4 bits are enough for up to 16 CPU's).
  • When one of the CPUs addresses matches the address of the locking circuit, and the corresponding clock goes high, the counter counts up by one if R/!W is high, and counts down if R/!W is low.
  • The counter value is decoded to the data bus. If the counter value is 0000, the CPU should read 00000000 binary, otherwise it may read any other value. Keep in mind, the 6502 reads the the data bus when the clock goes from high to low, so there is no race condition with the incrementation or decrementation of the counter.

Because of this, the lock can now be used by one of the CPUs to "take ownership" of e.g. a shared memory area or an I/O device. Whenever a CPU needs to store something in a shared memory area, it should:
  1. Read from the lock address (which increments the counter by one)
  2. Test if the value that was read is 0 (use the Z flag). Reading value 0 means the CPU "owns" the lock.
  3. If the CPU owns the lock, it can do with the shared memory area or I/O device what it wants, without a chance that another CPU will use it (as long as all software follows the rules of course). If the CPU does NOT own the lock, it should not attempt to access the memory location or I/O device at all.
  4. The CPU should write to the lock's location (to decrement the counter by one) regardless of whether it owned it or not.
  5. If the CPU didn't get to own the lock and still wants it, go to step 1.

The code to do this could look something like:
Code:
        JMP takeit

retry   STA lock        Decrease the counter
takeit  LDA lock        Increase counter. We own it if we made it 0
        BNE retry

        ; Do whatever is needed with the shared memory

leaveit STA lock

        ; Now other CPUs can take the lock


===Jac


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 8:47 am 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10981
Location: England
It might be worth thinking in terms of a memory system. The CPUs need access to the memory system, so they make requests. An arbiter in the memory system decides which CPUs get access. Other CPUs must be stalled.

In the case of a 6502 system, you'd try to do all that within one clock cycle - or, you'd use RDY to make the memory system multi-cycle and take more time. In general, you might have several memory cards or memory banks, which could have different access regimes. For example, if you design your software system such that shared memory is only a subset of memory, then that area gets more complex arbitration and possibly is a multi-cycle memory, whereas the remainder of memory has simpler arbitration and preferable can be a single-cycle memory.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 10:08 am 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
Quote:
if CPU A is executing an INC absolute of a location, any other CPU should ONLY be halted if it tries to access the SAME address. Accessing any other address is fine. But that would be a complicated circuit.


Luckily there's already something that does that in place.

The dual-port RAM already has built-in bus arbitration based around memory contention: if both Master and Slave attempt to access the same address at the same time, the bus arbiter will allow the "first" one to finish and inhibit the second by pulling BUSY low (which is connected to the relevant CPU's RDY pin). The problem is that this is done on an access-by-access basis, so that atomicity of RMW instructions are not protected: although the RMW instruction might win on one bus cycle, on the next it might not.

I guess that that means the following:
If Master's BUSY and Slave's ML are asserted at the same time, then Master's BUSY must continue to be asserted until Slave's ML is released, in order to guarentee that Master's accesses always win on every subsequent cycle, thus protecting the RMW's atomicity. And vice-versa.
This is probably a much more efficient idea, since a stall will only happen on actual memory contention, and the RAM's BUSY signal already ensures that only one of the two BUSYs will be asserted at one time, so we won't get a deadlock by making the continued assertion of BUSY dependent on BUSY being asserted in the first place.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
PostPosted: Fri Mar 23, 2018 2:24 pm 
Offline

Joined: Fri Mar 23, 2018 1:21 pm
Posts: 4
Sounds like a hardware semaphore might be needed. Can you have a flip-flop (or counter) set by a read operation to lock an address range for the memory operation ? Reading would return the previous value of the ff while setting it at the same time so that the next requester knows it’s busy. If the ff was clear then access the memory otherwise wait in a loop. Also requires the ability to write the ff to clear it. If the ff was on bit 7 of the databus then a BIT instruction could be used to test it.
Code:
.busy
BIT LockFF   ; the read will lock it
BMI .busy
 < access the memory >
STZ LockFF   ; clear semaphore


Top
 Profile  
Reply with quote  
PostPosted: Sat Mar 24, 2018 12:01 am 
Offline
User avatar

Joined: Tue Oct 25, 2016 8:56 pm
Posts: 362
There is a specific reason why I would like to guarentee the atomicity of RMW instructions, rather than simply banning their use on Shared RAM (or at least plastering it with warnings of "here be dragons"!)

Specifically, I want to ensure that TSB is always atomic, because I intend to use it as the basic operation for locking semaphores in software, see this page for explanation. This method only works if TSB is guarenteed to be atomic.

_________________
Want to design a PCB for your project? I strongly recommend KiCad. Its free, its multiplatform, and its easy to learn!
Also, I maintain KiCad libraries of Retro Computing and Arduino components you might find useful.


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 22 posts ]  Go to page 1, 2  Next

All times are UTC


Who is online

Users browsing this forum: No registered users and 13 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: