6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Mon Apr 29, 2024 8:15 am

All times are UTC




Post new topic Reply to topic  [ 3 posts ] 

Was this useful?
Yes 100%  100%  [ 5 ]
No 0%  0%  [ 0 ]
Total votes : 5
Author Message
PostPosted: Sat May 14, 2011 2:59 am 
Offline
User avatar

Joined: Fri Aug 30, 2002 9:02 pm
Posts: 1681
Location: Sacramento, CA
I was recently asked to help someone with an address decoder problem. Instead of just asking me to fix it, this person wanted some tips on how
to do it himself. I thought about the process I use to make address decoders and wrote a guide to help describe it. I'm including it here for
anyone interested.

Daryl

Code:
                         HOW TO BUILD LOGIC EQUATIONS FOR ADDRESS DECODERS


These are the logic symbols I use in this guide and are the ones used in WinCUPL.

! = NOT
& = AND
# = OR
$ = XOR

The order of operations of these logic operators is as listed above.  In order to override that order, you can use ( ) to group sections of equations to ensure the order you intended.
   i.e. A & B # C & D is the same as (A & B) # (C & D) but not the same as  A & (B # C) & D.

When I want to build logic equations for decoding memory, this is how I start:

1) Create a table of how the address space is to be used.

Example 1 : 32k RAM, 32K ROM, 2048 bytes I/O at $D000-$D7FF decoded to 1 output, active high

$0000-$7FFF = RAM
$8000-$CFFF = ROM
$D000-$D7FF = I/O pages
$D800-$FFFF = ROM

2) I then determine how many address lines I need to use to decode this.  Converting the
HEX addresses to binary can help.  Since my example does not break up any page boundary,
I can use just the upper 8 bits as a starting point:

.-- A15
|      .- A8       
|      |
00000000 - 01111111 - RAM
10000000 - 11001111 - ROM
11010000 - 11010111 - I/O Pages
11011000 - 11111111 - ROM

3) I can then examine the results and will reduce the number of bits needed if possible.  Examine the lowest bit first.
If all the ranges are 0 in the starting range and all the same ranges are 1 in the ending address, it is not needed.

My example gets reduced to this as the 3 lowest-ordered bits start with 0 and end with 1:

00000xxx - 01111xxx         00000 - 01111
10000xxx - 11001xxx    ->   10000 - 11001
11010xxx - 11010xxx         11010 - 11010
11011xxx - 11111xxx         11011 - 11111


4) Now I can start building equations. Start with AND statements using active-high outputs.

     Active-high means that the output is high (or 1) when the statement is true.  Consider the AND gate.  The output
     is high only when both inputs are high.

     A  B | O
     ---------
     0  0 | 0
     0  1 | 0
     1  0 | 0
     1  1 | 1

     Active-low simply means the output is low (or 0) when the statment is true.  Consider the NAND gate.  The
     output is low only when the two inputs are high.

     A  B | O
     ---------
     0  0 | 1
     0  1 | 1
     1  0 | 1
     1  1 | 0

     Thus, the difference between active high-and active-low.


Taking a quick look I can see that the highest bit is 0 only when RAM is selected, so I can simply write:
RAM = !A15

For ROM, I know I need to select it when the highest bit is 1, but I also need to not select it for the I/O pages.
In this case, it might be best to work on the I/O select first.  I see that the bits for I/O do not change from
starting to ending, so I can just write my equation to mirror the bit states:

IO = A15 & A14 & !A13 & A12 & !A11

Now for ROM, I can select it when A15=1 except for the IO pages.  I usually start with a two-part equation:
The first part is the logic to select ROM, the second part is the part I do not want to select. I put that part in
parenthesis preceeded by a ! (or NOT symbol).

ROM = A15 & !(A15 & A14 & !A13 & A12 & !A11)

This reads as "Select ROM when A15 is high but NOT when A15 & A14 & !A13 & A12 & !A11."

5) I can now adjust each equation for the active output level.

The RAM chip select is active-low so:

RAM = !A15 -> !RAM = !A15.  This can also be written as RAM = A15, but this can confuse some folks as it appears
you want RAM to be in the upper half of memory as we tend to see all outputs as active-high.  For the computer,
either one is correct.

I will leave the I/O select active-high as this will most likely be used to gate other lower address lines
to provide individual I/O device selects.

IO = A15 & A14 & !A13 & A12 & !A11


ROM will use an active-low output so this becomes:

!ROM = A15 & !(A15 & A14 & !A13 & A12 & !A11)

SO, these are our final equations:

!RAM = !A15
IO = A15 & A14 & !A13 & A12 & !A11
!ROM = A15 & !(A15 & A14 & !A13 & A12 & !A11)

You can use these directly in your programmable logic, or you can choose to reduce then.  I would recommend leaving them
in this format as it will be easier to understand when you have to come back to it down the road.

Now I will give a few more examples, showing different combinations.  When designing a new system, you will want
to consider address decoding up front in terms of space required for each type of memory, location of each, and
the number address lines needed to arrive at that goal.

Example 2: 32k RAM, 32K ROM, 256 byte I/O at $02xx, decoded for 4 devices, all active-low outputs

$0000-$01FF = RAM
$0200-$023F = IO1
$0240-$027F = IO2
$0280-$02BF = IO3
$02C0-$02FF = IO4
$0300-$7FFF = RAM
$8000-$FFFF = ROM

This time I do cross page boundaries so I will start with all 16 bits of the address.

00000000 00000000 - 00000001 11111111 - RAM
00000010 00000000 - 00000010 00111111 - IO1
00000010 01000000 - 00000010 01111111 - IO2
00000010 10000000 - 00000010 10111111 - IO3
00000010 11000000 - 00000010 11111111 - IO4
00000011 00000000 - 01111111 11111111 - RAM
10000000 00000000 - 11111111 11111111 - ROM

I remove un-needed bits:

00000000 00xxxxxx - 00000001 11xxxxxx - RAM
00000010 00xxxxxx - 00000010 00xxxxxx - IO1
00000010 01xxxxxx - 00000010 01xxxxxx - IO2
00000010 10xxxxxx - 00000010 10xxxxxx - IO3
00000010 11xxxxxx - 00000010 11xxxxxx - IO4
00000011 00xxxxxx - 01111111 11xxxxxx - RAM
10000000 00xxxxxx - 11111111 11xxxxxx - ROM

Start mapping - easy one first:

ROM = A15

IO Next

IO1 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 & !A7 & !A6
IO2 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 & !A7 &  A6
IO3 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 &  A7 & !A6
IO4 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 &  A7 &  A6

RAM last.  Since all of page 2 is IO, you don't need to include A7 and A6

RAM = !A15 & !(!A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8)

Now adjust for active outputs.  All these will be active low.

!ROM = A15
!IO1 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 & !A7 & !A6
!IO2 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 & !A7 &  A6
!IO3 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 &  A7 & !A6
!IO4 = !A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8 &  A7 &  A6
!RAM = !A15 & !(!A15 & !A14 & !A13 & !A12 & !A11 & !A10 & A9 & !A8)


Example 3: 48k RAM (using two RAM devices), 16k ROM, and 2 I/O at $C0xx with active-low outputs.

$0000-$7FFF = RAM1
$8000-$BFFF = RAM2
$C000-$C07F = IO1
$C080-$C0FF = IO2
$C100-$FFFF = ROM

00000000 00000000 - 01111111 11111111 - RAM1
10000000 00000000 - 10111111 11111111 - RAM2
11000000 00000000 - 11000000 01111111 - IO1
11000000 10000000 - 11000000 11111111 - IO2
11000001 00000000 - 11111111 11111111 - ROM

00000000 0xxxxxxx - 01111111 1xxxxxxx - RAM1
10000000 0xxxxxxx - 10111111 1xxxxxxx - RAM2
11000000 0xxxxxxx - 11000000 0xxxxxxx - IO1
11000000 1xxxxxxx - 11000000 1xxxxxxx - IO2
11000001 0xxxxxxx - 11111111 1xxxxxxx - ROM

RAM1 = !A15
RAM2 =  A15 & !A14
IO1 = A15 & A14 & !A13 & !A12 & !A11 & !A10 & !A9 & !A8 & !A7
IO2 = A15 & A14 & !A13 & !A12 & !A11 & !A10 & !A9 & !A8 &  A7
ROM = A15 & A14 & !(!A13 & !A12 & !A11 & !A10 & !A9 & !A8)

!RAM1 = !A15
!RAM2 =  A15 & !A14
!IO1 = A15 & A14 & !A13 & !A12 & !A11 & !A10 & !A9 & !A8 & !A7
!IO2 = A15 & A14 & !A13 & !A12 & !A11 & !A10 & !A9 & !A8 &  A7
!ROM = A15 & A14 & !(!A13 & !A12 & !A11 & !A10 & !A9 & !A8)

This takes a little practice, but in time becomes easier to understand. Make a few examples
of your own and give it a try.


Last edited by 8BIT on Sat May 14, 2011 3:07 pm, edited 1 time in total.

Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat May 14, 2011 8:47 am 
Offline

Joined: Tue Jul 05, 2005 7:08 pm
Posts: 990
Location: near Heidelberg, Germany
Nice introduction, thank you for sharing!

Two comments:
1) I know that I do multiply before I add, but I always struggle if I do AND before OR (right?). Maybe you could add a statement about the priority of the logic operations. E.g. NOT is done first, then ...

2) You switch to binary representation to determine which address bits are actually relevant, which is ok. But then you switch back to discuss "address windows". That works ok for your examples, where you basically have only one I/O windows.

I rather like to break down the addresses on the binary level, starting from the higher address bits down to the lower ones. So for example you first ROM area becomes
Code:
ROM1=(A15 & !A14)
          # (A15 & A14 & !A13 & !A12)
ROM2=(A15 & A14 & !A13 & A12 & A11)
          # (A15 & A14 & A13)

where in the first term the first part is the 16k area from $8000 to $BFFF, and the second part is $C000-$CFFF. The second term (for ROM2) then has two parts, for $D800-$DFFF and $E000-$FFFF.

Breaking down the addresses like that "reduces" the terms to normal form which allows you to do optimizations in your address decoding. If you would use for example two different ICs or other memory-mapped devices (so you would not combine ROM1 and ROM2 as you did in your example), you could for example reduce the number of terms by moving the area at $D800 to something closer to a say a 16k boarder.

Ok, thinking about it now this is probably more like "old school", where the address decoding is done in discrete logic, and not in programmable logic, and you more needed to optimize e.g. due to timing requirements The resulting decoding scheme is the same anyway and the "normal" form is less grokable than your address window form.

Probably you could just emphasize the switch between the different representations.

André


Top
 Profile  
Reply with quote  
 Post subject:
PostPosted: Sat May 14, 2011 3:13 pm 
Offline
User avatar

Joined: Fri Aug 30, 2002 9:02 pm
Posts: 1681
Location: Sacramento, CA
Thanks for the feedback. I added the order of operations as you suggested. I also added a little more references for the binary mapping.

As far as the equations go, there are several ways to approach it and your way yields the same results. As others start to use programmable logic, they will each find the method that works for them.

I am showing one possible way and don't want to confuse the issue for beginners by adding too many options.

Thanks again for the feedback!

Daryl


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 3 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 7 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: