skip navigational linksPJRC
Shopping Cart Checkout Shipping Cost Download Website
Home MP3 Player 8051 Tools All Projects PJRC Store Site Map
You are here: MP3 Player Technical Docs Memory Map

PJRC Store
Main Board, $150
LCD & Pushbuttons, $42
LCD/Backlight/PB, $77
IDE Cable, $9
Complete Parts List
MP3 Player
Main Page
Detailed Info
User Photo Gallery
Connecting The Board
Firmware Download
Side Projects
Technical Docs
Freq. Asked Questions
FAQ #2
News And Updates

External Memory Map (Accessed with MOVX)

Status Codes

  • Working - A reasonable number of tests have been done and this feature appears to be working properly.
  • Looking Good... - Working in initial tests, but not enough tests have been done to know if it really works well.
  • Untested - It's implemented and ought to work, but no tests have been done yet to see if it's actually working
  • Unimplemented - Some piece of hardware is still missing to allow this feature to work... but since it's on this list, at least some of the hardware is in place.
  • Missing - None of the hardware exists yet. These appear on a list at the bottom, because their functionality is not yet well defined enough to be added to the register list.

Address Allocation Description
0x0000 - 0x4FFF Static Static variables (20k) available for the application level code
0x5000 - 0x7FFF Dynamic Three pages (8k) of space available for the application to use dynamic memory (mapping pages obtained from malloc_blocks)
0x8000 - 0x8FFF Static Variables for FAT32, IDE, Playback, Memory Manager
0x9000 - 0x9FFF Dynamic Playback DMA buffer
0xA000 - 0xAFFF Dynamic IDE DMA buffer
0xB000 - 0xBFFF Dynamic FAT32 buffer
0xC000 - 0xCFFF None Unused
0xD000 - 0xDFFF Static FAT32 cached FAT sector list
0xE000 - 0xEFFF Dynamic Memory manager buffer (typically maps blocks 0-31, which contain allocation and usage data for all other blocks)

Name Data Type* Address Status Description
PAGE_0 memory 0x0000 to 0x0FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_1 memory 0x1000 to 0x1FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_2 memory 0x2000 to 0x2FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_3 memory 0x3000 to 0x3FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_4 memory 0x4000 to 0x4FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_5 memory 0x5000 to 0x5FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_6 memory 0x6000 to 0x6FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_7 memory 0x7000 to 0x7FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM
PAGE_8 memory 0x8000 to 0x8FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM

This page is reserved for misc static data structures used by library routines. File descriptor tables, IDE and playback pending request queues, etc.

PAGE_9 memory 0x9000 to 0x9FFF Working DRAM Memory, may be mapped to any 4k range within the SIMM

This page is reserved for playback. The playback code maps the currently playing block here, until the DMA transfer to the MP3 decoder is complete.

PAGE_A memory 0xA000 to 0xAFFF Working DRAM Memory, may be mapped to any 4k range within the SIMM

This page is reserved for use by the the IDE driver. The IDE driver maps blocks here for DMA transfers.

PAGE_B memory 0xB000 to 0xBFFF Working DRAM Memory, may be mapped to any 4k range within the SIMM

This page is reserved for use by the the FAT32 filesystem. Typically, the FAT32 code will map blocks containing FAT sectors when tring to figure out what clusters compose a file, and to copy the data from the cached clusters when the application reads the file.

PAGE_C memory 0xC000 to 0xCFFF Working DRAM Memory, may be mapped to any 4k range within the SIMM

This page isn't currently being used.

PAGE_D memory 0xD000 to 0xDFFF Working DRAM Memory, may be mapped to any 4k range within the SIMM

This page is reserved for use by the FAT32 filesystem. This page holds an array of block numbers which are being used to cache FAT sector groups.

PAGE_E memory 0xE000 to 0xEFFF Working DRAM Memory, may be mapped to any 4k range within the SIMM

This page is reserved for use by the memory manager (malloc, et all). The memory manager will usually map one of the first 32 blocks, which contain a linked list and misc data about all of the 4k blocks (16 bytes per block).

DRAM_PAGE_CFG 16 bit INT (15) 0xFF00 to 0xFF1D Working These fifteen integers control which of the 4k pages of the DRAM is seen in each of the 4k pages in the processor's 4k pages (PAGE_X above). Each of the supported SIMM sizes provides the following blocks:
  • 4 MByte SIMM
    • 0 to 1023
  • 8 MByte SIMM
    • 0 to 2047
  • 16 MByte SIMM
    • 0 to 1023
    • 2048 to 3071
    • 4096 to 5119
    • 6144 to 7167
  • 32 MByte SIMM
    • 0 to 8191
4 and 8 meg SIMM sizes will repeat (duplicate) 4 times though the remaining blocks. A 4 meg SIMM, for example, would appear to have memory at block 2048, but writing there would overwrite the contents of block 0.
DMA_IDE_DEST 16 bit INT 0xFF22 to 0xFF23 Working Destination address for DMA transfer from the IDE interface to the DRAM. The 16 bit address must be between 0000 to EFFE. The page(s) where the DMA will write data must remain mapped (DRAM_PAGE_CFG) during the entire transfer. Only even addresses should be used. This register is incremented by the hardware as the DMA transfer takes place
DMA_IDE_COUNT 16 bit INT 0xFF24 to 0xFF25 Working Specifies the number of 16 bit words to transfer from the IDE interface to DRAM. To transfer a 512 byte sector, this would be initialized to 256. This register is decremented as the transfer takes place and it will read zero when the transfer is complete. An interrupt may also be generated upon completion.
DMA_MP3_SRC 16 bit INT 0xFF28 to 0xFF29 Working Source address for DMA transfer from the DRAM to the STA013 MP3 decoder chip. The hardware automatically transfers the data as the STA013 is ready for it. This register is incremented by the hardware as the DMA transfer takes place. The page(s) containing this memory need to remain mapped during the transfer.
DMA_MP3_COUNT 16 bit INT 0xFF2A to 0xFF2B Working Specifies the number of 16 bit words to transfer from DRAM to the STA013 MP3 decoder chip. This register is decremented as the transfer takes place and it will read zero when the transfer is complete. An interrupt may also be generated upon completion.
MEMCPY_DEST 16 bit INT 0xFF30 to 0xFF31 unimplemented Don't use the fast hardware memcpy yet!
MEMCPY_SRC 16 bit INT 0xFF32 to 0xFF33 unimplemented Don't use the fast hardware memcpy yet!
MEMCPY_COUNT 16 bit INT 0xFF34 to 0xFF35 unimplemented Don't use the fast hardware memcpy yet!
IDE_RESET_BIT 1 bit 0xFF40 Working The LSB at this location controls the IDE reset pin. Write 0 to reset the IDE devices, or write 1 for normal operation. This bit is initialized to zero when the FPGA is configured, and must be written as 1 to allow the IDE devices to operate. The upper 7 bits will read random data, and should always be written as zeros.
IDE_DATA_BUF_LSB 8 bit BYTE 0xFF42 untested Reading this regsiter will return the lower 8 bits of data from the previous IDE interface read. Writing will store the lower 8 bits to be written on the next IDE write. Only the buffers within the FPGA are accessed, no IDE activity is generated by accessing this register. Separate buffers are used within the FPGA, so a write to this register followed by a read will not necessarily return the same byte. This register will not normally be needed, as reads and writes to the IDE port at even addresses will read/write the lower byte of the IDE devices.
IDE_DATA_BUF_MSB 8 bit BYTE 0xFF43 Working Reading this regsiter will return the upper 8 bits of data from the previous IDE interface read. Writing will store the upper 8 bits to be written on the next IDE write. Only the buffers within the FPGA are accessed, no IDE activity is generated by accessing this register. Separate buffers are used within the FPGA, so a write to this register followed by a read will not necessarily return the same byte. This register will normally be used when transfering data without DMA, where it would be written before a write to the IDE port or read after a read from the IDE port.
IRQ_IDENT 8 bit BYTE 0xFF50 Working This read-only register tells which interrupts are currently pending. Reading it does not clear an interrupt. This register should be read at the beginning on the interrupt service routine to determine which actual interrupt occured.

  • Bit 0: DMA transfer from IDE interface completed
  • Bit 1: DMA transfer to MP3 decoder completed
  • Bit 2: memcpy operation completed
Unused bits will always read as zeros. If more interrupts are added in the future, these bits will be defined.
IRQ_MASK 8 bit BYTE 0xFF51 Untested This write-only register configure which bits in the interrupt register can assert the 8051's INT0 pin. The bits are set by completion of a DMA transfer, and cleared by writing to the "ack" registers, regardless of what is written here. This register allows the individual interrupt to be masked from asserting INT0, but they otherwise function in exactly the same way.

  • Bit 0: DMA transfer from IDE interface
  • Bit 1: DMA transfer to MP3 decoder
  • Bit 2: memcpy operation
DMA_IDE_GO Flag 0xFF58 Working Writing to this location causes a DMA transfer from the IDE interface to DRAM memory to begin. DMA_IDE_DEST, DMA_IDE_COUNT, and the corresponding page select in DRAM_PAGE_CFG need to be set up, and it also helps if the drive is ready to transfer data from a command written via IDE_PORT. Data is transfered directly from the drive to DRAM. The drive "sees" PIO style access. Data transfer speed should be approx 2.4 Mbytes/sec.
DMA_MP3_GO Flag 0xFF59 Working
Writing to this location causes a DMA transfer from the DRAM to MP3 decoder chip. DMA_MP3_SRC and DMA_MP3_COUNT need to be set up before writing here.
MEMCPY_GO Flag 0xFF5A Unimplemented
(may crash if used)
Don't use the hardware memcpy yet... just do it in code for now.
IRQ_DMA_IDE_ACK Flag 0xFF5C Working Writing to this location causes bit 0 in IRQ_IDENT to be cleared. This must be done in the interrupt service routine if bit 0 is set.
IRQ_DMA_MP3_ACK Flag 0xFF5D Working Writing to this location causes bit 1 in IRQ_IDENT to be cleared. This must be done in the interrupt service routine if bit 1 is set.
IRQ_MEMCPY_ACK Flag 0xFF5E Untested Writing to this location causes bit 2 in IRQ_IDENT to be cleared. This must be done in the interrupt service routine if bit 2 is set.
IDE_PORT 8 bit Bytes (see desc) 0xFF60 to 0xFF7F Working This area provides read/write access to the IDE interface. All reads within this range cause an IDE read cycle, and all writes cause an IDE write cycle. Access to the even addresses within this range read/write the lower 8 bits of the IDE port, and the upper 8 bits are from IDE_DATA_BUF_MSB are used. Likewise, access to the odd locations read/write the upper 8 bits and IDE_DATA_BUF_LSB is used for the lower 8 bits. The first 16 bytes access the IDE interface via CS0, and the second 16 access it via CS1.

Normally only some of the even addresses will be used, because the upper data byte of the IDE interface are only used for data transfer, not commands. Most IDE registers are accessed with CS0, only "control" and "alt_status" use CS1. Here's the list of registers that would normally be used.

  • 0xFF60 - Data read/write (use with IDE_DATA_BUF_MSB)
  • 0xFF62 - Error Code (read)
  • 0xFF64 - Sector Count (write)
  • 0xFF66 - Sector (write)
  • 0xFF68 - Cylinder, LSB (write)
  • 0xFF6A - Cylinder, MSB (write)
  • 0xFF6C - Head (write)
  • 0xFF6E - Status (read), Command (write)
  • 0xFF7C - Control (write)
  • 0xFF7C - Alt Status (read)
Ethernet
(Tom did some work on an ethernet add-on a couple years ago, but that project is more or less dead now. These addresses could be reclaimed for something else if anyone wants them.)
TBD 0xFF80 to 0xFF8F Unimplemented This area is reserved for Tom's Ethernet project. The tenative register list for a Cirrus CS8900A chip is:
  • FF80 - Receive/Transmit Data (port 0)
  • FF82 - Receive/Transmit Data (port 1)
  • FF84 - Transmit Command (TxCMD)
  • FF86 - Transmit Length (TxLength)
  • FF88 - Interrupt Status Queue
  • FF8A - PacketPage Pointer
  • FF8C - PacketPage Data (Port 0)
  • FF8E - PacketPage Data (Port 1)
DRAM_WR_DIS Flag 0xFFC0 Working Writing to this location disables writing to the DRAM memory. This is done by the Flash ROM programming code within PAULMON2_MP3, so that the DRAM doesn't "hear" the writes to the flash memory. The code with PM2 also enables DRAM writing after the flash programming.
DRAM_WR_EN Flag 0xFFC1 Working Writing to this location enables writing to the DRAM memory. The FPGA is initialized with DRAM writing enabled, so it is not necessary to write here unless DRAM writing has been disabled.

* Data Type: "16 bit INT" types are also stored as "little endian", where the LSB is stored at the first location and MSB are the second. "Flag" types are special locations that cause an action when written. They do not have data associated with them, so any byte may be written (whatever's in the accumulator at the moment?).

Missing Stuff

  • Tri-state all the IDE interface pins when the PC power sense pin is pulled low. There needs to be a way for the firmware to know this has happened.
  • Interrupt mask. Right now there's no way to mask the individual interrupt sources. The current firmware (0.5.0) does polling by reading IRQ_IDENT and ignores the interrupt pin.

TODO: add memory map for code space.... explain bank swap, normal code goes in bank 0, how to properly call to and return from code in bank 1, how to get code to download to the correct bank, and possibly other issues I missed in this list.

TODO: explain how the DMA works. General process: do setup for that source has data, make sure pages are properly mapped, set up DMA parms, write to go flag, when interrupt happens it's done, clear interrupt with ack flag. All DMA channels are separate and can be used concurrently (sending data to MP3 decoder while also transfering sectors from the drive). DRAM is still accessible during DMA transfers, and it's ok to remap pages that aren't involved in the transfer.

IDE Port, 16 Bit Access

Here's a copy of a message I wrote to help clarify issues associated with accessing the IDE port in 16 bit PIO mode.

Accessing 16 bit data from the IDE port is tricky. Usually you would read IDE_DATA (0xFF60) first to get the LSB. Reading this register causes all 16 bits to be read into the FPGA's 16-bit buffer for the IDE port, so the upper 8 bits may be read using the IDE_DATA_BUF_MSB (0xFF43) register.

The tricky part is that any 16 bit access to or from the IDE drive or the DRAM will reuse the buffer and overwrite its contents. So, if you have code like this:

    xdata unsigned char my_lsb, my_msb;
    my_lsb = read_fpga_reg(0xFF60);
    my_msb = read_fpga_reg(0xFF43);
It will not work. The first line reads the IDE_DATA port, and then the byte is stored in variable "my_lsb", which is in xdata memory (DRAM), so the writing operation to my_lsb will destroy the contents of the 16 bit buffer that is read in the next line. If you replace this code with something like:
    data unsigned char my_lsb, my_msb;
    my_lsb = read_fpga_reg(0xFF60);
    my_msb = read_fpga_reg(0xFF43);
Now the two bytes are allocated in the 8051's internal memory, so the write operation to "my_lsb" will not destroy the 16-bit buffer contents. However, this _still_ won't work, because an interrupt could occur anytime between the two MOVX instructions that will ultimately be executed within the two calls to read_fpga_reg, and nearly all of the interrupt routines will access the DRAM memory. So if you disable interrupts, like this:
    data unsigned char my_lsb, my_msb;
    EA = 0;
    my_lsb = read_fpga_reg(0xFF60);
    my_msb = read_fpga_reg(0xFF43);
    EA = 1;
It probably work, but it will still fail if there is a DMA transfer underway, as these DMA transfers also access DRAM and reuse that 16-bit buffer.

I don't currently have a reasonable way to work around the DMA access... so this is a pretty serious limitation which will need to be addressed in the hardware to enable you to make use of 16 bit PIO access to the IDE interface.

One thing that might make you life a bit easier is using some C syntax to directly access the registers, instead of having to call assembly language helper functions. You can declare the FPGA registers like this:

    volatile xdata unsigned char at 0xFF60 reg_ide_data;
    volatile xdata unsigned char at 0xFF43 reg_ide_buf_msb;
and then you can access them just like C variables. Be sure to use the "volatile" keyword, so that the C compiler doesn't try to optimize out your accesses to the registers.

Flash ROM (Code) Memory Allocation

There are two banks of 56k (the first 8k is always consumed by the image of the monitor in the 87C52's internal EPROM). Here's how the flash rom memory is allocated today (June 4, 2002):

BankBeginEndUsage
0 20002FFFInterrupt Routines
0 3000DFFFC Code
0 E000ECFFUtility Programs (non-essential)
0 ED00EFFFUtility Programs (the 'X', 'Y' erase and auto-start)
0 F000FFFFFirmware Rev is 0.1.3 (which is run if there is no simm)
1 20002FFFInterrupt Routines (must be in both banks)
1 30007FFFDevice Drivers
1 80008FFFNon-Volatile Params
1 90009FFFSTA013 Config Data
1 A000CEFFFPGA Config For 0.6.x
1 CF00CFFFUnused (256 bytes)
1 D000FEFFFPGA Config For 0.1.3
1 FF00FFFFHelper code for the 'X' and 'Y' erase


MP3 Player, Memory Map and Hardware Registers, Paul Stoffregen.
http://www.pjrc.com/tech/mp3/mem_map.html
Last updated: February 23, 2005
Questions, Comments?? <paul@pjrc.com>