28
Memory controller for a 6502 CPU in VHDL Final report for in3019p ‘Bachelor project’ TU Delft, faculty of EEMCS Michel Wilson, 1047981 [email protected] May 2006 Project client Embedded Software Lab, TU Delft Client contact A.J.C. van Gemund Coordinator B.R. Sodoyer

Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Embed Size (px)

Citation preview

Page 1: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Memory controller for a 6502 CPU in VHDL

Final report for in3019p ‘Bachelor project’TU Delft, faculty of EEMCS

Michel Wilson, [email protected]

May 2006

Project client Embedded Software Lab, TU DelftClient contact A.J.C. van GemundCoordinator B.R. Sodoyer

Page 2: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Abstract

The 6502 soft core implemented in VHDL on an FPGA development board as used by theEmbedded Software Lab of the Delft Technical University currently only uses on-chip RAM. Thisleads to a shortage of memory in certain situations. To solve this problem, a memory controllerfor the soft core which enables access to the external RAM chips available on the developmentboard has to be developed. This project describes the design and the development of such acontroller, and an accompanying program to test the working of the controller and the memory.

Page 3: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Contents

Contents 1

1 Introduction 31.1 About the project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31.2 About this document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

2 Action plan 42.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.1.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.1.2 Approval and adjustment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4

2.2 Project description . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.2.1 Project goals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.2.2 Milestones and deliverables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42.2.3 Requirements and constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

2.3 Project phases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.3.1 Requirements analysis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.3.2 Design decisions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.3.3 Development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.3.4 Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52.3.5 Final product . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5

3 Architecture and design decisions 63.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63.2 Existing situation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63.3 New situation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73.4 Memory controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

3.4.1 Address space distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.4.2 Addressing modes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.4.3 Timing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

3.5 Address translator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93.6 Memory test algorithm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.6.1 Addressing test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113.6.2 Data test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4 Code documentation 124.1 VHDL code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

4.1.1 Toplevel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124.1.2 Memory management unit . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124.1.3 Memory controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144.1.4 I/O controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4.2 Memory test program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

5 Conclusion and recommendations 22

1

Page 4: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

A Working with the FPGA under Linux 23A.1 USB to Serial Converter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23A.2 Xilinx software . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

B External memory timing diagrams 25

Bibliography 26

2

Page 5: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

C H A P T E R 1

Introduction

1.1 About the project

The Embedded Software Lab of the Delft Technical University, which is part of the SoftwareEngineering Department, uses a 6502 soft core, implemented in VHDL in a Digilent Spartan-3Development Board. A limitation of the soft core is the limited amount of available memory.The current design only uses the RAM available on the FPGA chip. The aim of the project is todesign and develop a memory controller for the soft core, which enables access to the externalRAM chips on the development board. A secondary goal of the project is to create a program forthe 6502 which can assist in testing the integrity of the memory controller, and the external RAM.The project is completed in connection with the requirements of the course in3019p, ‘BachelorProject’.

1.2 About this document

In this document, the final result of the project is presented, along with the road towards this finalresult. In three chapters, the documents which were produced during the projects are included.The action plan describes the goal of the project, and its phases; the architecture and design decisionsdescribes the technical implementation of the controller on a high level; and finally, the technicaldocumentation describes the source code of the final product in detail. The final chapter containssome concluding remarks about the project. Lastly, some remarks and instructions about workingwith the FPGA board and the Xilinx software under Linux are included in the appendix.

3

Page 6: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

C H A P T E R 2

Action plan

2.1 Introduction

2.1.1 Motivation

Currently, a 6502 soft core is used in the Embedded Software lab, which is implemented in VHDLon a Digilent Spartan-3 Development Board, using the Xilinx ISE WebPACK design software ([3]).The soft core is used for research, and for the course in4073 (Embedded Real-Time Systems). Allprograms using this core are severely constrained by the limited amount of available memory inthe FPGA chip on the Development Board. The primary goal of the project is therefore to increasethe amount of memory available to the soft core, using the external memory available on thedevelopment board.

2.1.2 Approval and adjustment

The action plan should be approved by the client. During the work on the project, progress shouldbe reported. Any adjustments to the action plan will be contained in the progress reports. Uponapproval of the progress reports, the original action plan is implicitly adjusted and approved aswell. The actual action plan thus consists of the original document and all progress reports.

2.2 Project description

2.2.1 Project goals

The single goal of the project is increasing available memory of the soft core. To attain this goala memory management unit (MMU) should be developed which will act as an interface betweenthe soft core and the external RAM which is available on the development board. The MMUshould be a reusable component. As the available external memory is larger than the addressspace of the current soft core, some form of bank switching should be implemented.

Secondly, a test program should be built, demonstrating the functionality of the MMU andvalidating that the component is indeed working. The test program should provide a reliableindication if the MMU and memory are working or faulty.

2.2.2 Milestones and deliverables

The first milestone in the project is the specification of the architecture of the system. Thismilestone is reached after the various design decisions are made. The deliverable is the VHDLentity description, accompanied by a document detailing the design decisions made and therationale behind them. In this document, the methodology of the test program should also bedescribed, together with an explanation why this is a valid testing method.

The second, and final milestone is reached upon completion of the project. The main deliver-able is the VHDL source code for the memory management unit. It should consist of a generalentity which communicates with the external memory, and a translation unit which maps thememory into the address space of the soft core, including the handling of bank switching. Cleardocumentation of the usage, but also of the inner workings, of these entities should be provided.

4

Page 7: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

The other deliverable is the C source code of the test program, also including documentation,explaining how to use the program, and explaining why the test procedure is deemed to be anaccurate indication of the working or failure of the MMU and/or the memory.

2.2.3 Requirements and constraints

The main requirement for the project is the reliability. This requirement has priority over all otherrequirements. If some implementation of another requirement endangers the reliability of theproject the offending requirement should be implemented in a different fashion, not endangeringthe primary requirement, or dropped althogether. The project’s conformance to this requirementcan be checked using the test program. This also implies that the reliability (correctness) of the testprogram is a main requirement. The conformance to this requirement should be demonstratedby an in-depth analysis of the method the test program uses to perform its function.

Further requirements of the memory management unit are adequate performance, and smallresource usage. A suitable balance of these two has to be found, but in general it should besufficient to be able to always either read or write a memory location without introducing waitcycles in the processor. The total resource usage of the memory management unit should be keptwithin reasonable bounds, but it should not be necessary to use extreme measures to obtain thelowest possible resource usage.

2.3 Project phases

2.3.1 Requirements analysis

The requirements and constraints for the design are specified, in consultation with the client. Mostrequirements/constraints are already specified in this document, so this is not really a separatephase.

2.3.2 Design decisions

Decisions are made on specific implementation details, and documented including the rationalebehind them. In particular, the (external) interfaces of the VHDL entities can be specified in thisphase. Also, decisions are made on how to interface with the external memory.

2.3.3 Development

The actual implementation takes place in this phase. If any design decisions from the previousphase are found to be infeasible, an alternative decision is to be sought, in consultation with theclient. The documentation of the previous phase should be updated in this case.

2.3.4 Testing

All implemented systems are tested, to confirm that they indeed perform according to the speci-fication. Any inconsistencies are to be corrected.

2.3.5 Final product

The final product consists of the deliverables as mentioned above, and a report on the developmentprocess, including all documents which are produced.

5

Page 8: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

C H A P T E R 3

Architecture and design decisions

3.1 Introduction

In this chapter, the proposed architecture of the memory management unit extension for the6502 softcore will be described, along with the design decisions which were made to reach thisproposal. Also, the choice of the testing algorithms will be discussed. The VHDL code wasdeveloped using the Xilinx ISE WebPACK design software, version 7.1i, under Linux ([3].

As there is a clear distinction between interfacing with the external memory, and interfacingwith the rest of the processor, the memory controller will be divided into a part which accessesthe external memory, and a part which translates the memory address. These components willbe discussed in detail, below. Lastly, the test program will be discussed.

3.2 Existing situation

In the existing situating the system consists of the CPU itself, and the memory management unit.Both are connected to the central clock. From the central clock, the cpu_halt signal is derived,which has half the clock speed. This signal controls the activation of the CPU and the MMU.When the CPU is not enabled, the MMU is enabled, and vice versa.

The interface between the CPU and the MMU consists of an address bus, read/write signals,and data busses in both directions. The MMU partitions the address space in blocks of 4kB, intowhich separate components are mapped. For the mapping in the existing situation, see table 3.1.Essentially, the MMU functions as a kind of multiplexer.

11..0

15..12

MMU

.

.

.

.

.

.

RAM

I/O

ROM

RAM

address

read flag

write flag

data

CPU

Figure 3.1: Block diagram of the existing situation

6

Page 9: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

11..0

15..12

MMU

.

.

.

.

.

.

RAM

I/O

ROM

RAM

address

read flag

write flag

data

controllerMemory

CPU Externalmemory

Figure 3.2: New situation

3.3 New situation

In the new situation, an extra component is added to the MMU, which can address the externalmemory chips. This component is mapped to several address blocks, from 5000h to CFFFh. Thenew mapping is also displayed in table 3.1.

As the external memory is bigger than the available address space, bank switching is used.To select the bank to map to the internal address space, a register is added to the I/O component,at address 500h, which is subsequently mapped to E500h. Writing to this register switches tothe specified portion of the external memory. Reading the register gives the currently mappedmemory bank.

The setup of the MMU dictates the signals of the memory controller on the side of the MMU.The same in/outputs are used as on the other components, refer to table 3.2 for the details. Theonly difference is in the size of the address signal. The existing components have an addresssignal of 12 bits, since they are mapped into a region of 4kB. As the memory controller is mappedinto 8 such regions, 15 bits are needed for this. Furthermore, the memory controller needs toknow the current page, from the I/O controller. For simplicity, the 5 bits of the page address areappended to the address signal, resulting in an address signal of 20 bits.

The setup of the memory chips dictates the signals of the memory controller on the other side.The board contains two memory chips. There is an enable signal for each chip, a signal to selectthe upper or lower byte, and the data lines. Shared among the chips is the output enable, thewrite signal, and the address. See table 3.3 for more details.

3.4 Memory controller

The task of the memory controller is interfacing with the external memory. It takes an address,a direction (read or write), and the actual data, and reads/writes it to the external memory. Allthe details of how the memory should be accessed, like timing, are taken care of by the memorycontroller. More information about the memory chips can be found in the datasheet (see [1]).

7

Page 10: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Address Existing situation New situation

0000h internal RAM internal RAM1000h internal RAM internal RAM2000h internal RAM internal RAM3000h internal RAM internal RAM4000h internal RAM internal RAM5000h unused external RAM6000h unused external RAM7000h unused external RAM8000h unused external RAM9000h unused external RAMA000h unused external RAMB000h unused external RAMC000h unused external RAMD000h unused unusedE000h I/O I/OF000h ROM ROM

Table 3.1: Address space of the 6502 softcore

Signal Function

CLK Central 50MHz clockhalt Enable/disable the controller, see also timing sectionread_enable Read the selected memory addresswrite_enable Write the selected memory addressaddress (19 downto 0) Memory addressdata_in (7 downto 0) Data to memorydata_out (7 downto 0) Data from memory

Table 3.2: Signals for the memory controller, MMU side

Signal Function

mem_enable_1Chip enable signals

mem_enable_2

mem_upper_1 Enabling/disabling of the upper output byte of the memorychipsmem_upper_2

mem_lower_1 Enabling/disabling of the lower output byte of the memorychipsmem_lower_2

mem_out_enable Output enablemem_wr_enable Write enablemem_address (17 downto 0) Memory addressmem_data_1 (15 downto 0)

Data linesmem_data_2 (15 downto 0)

Table 3.3: Signals for the memory controller, external side

8

Page 11: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

3.4.1 Address space distribution

As there are two different memory chips, the addresses can be distributed in different ways. Thetwo choices are to distribute the data linearly over the two chips, or to stripe the data across thetwo chips with some block size. The striping approach can be used to increase performance whenaccessing the memory sequentially. In this case however, the memory speed is high enough toachieve what we want, so striping is not necessary. Therefore, the data is distributed linearly overthe two chips, using the MSB to select the chip to use.

As the chips have a 16 bits wide data path, the LSB is used to drive the signals which selectthe upper/lower byte, and to select the required data signals. The MSB is used to select one of thetwo memory chips. A 4-way multiplexer is thus needed, using the MSB and the LSB to select oneout of four 8 bit words from the data lines.

3.4.2 Addressing modes

The memory chips can be addressed in several modes. Due to the fact that the CPU uses 8 bits,and the memory uses 16 bits, using the mem_upper/mem_lower signals to control the memory isnecessary when writing. Write cycle 4 is thus the most logical choice (see figure B.2 on page 25).When reading, these signals are not really needed, because we are using a multiplexer anyway, toselect the right data bits. Since there is no reason (and no way) to switch off the memory betweenreads, read cycle 1 is the only choice for reading (see figure B.1 on page 25).

3.4.3 Timing

In the existing system, the CPU and the memory system are enabled every other clocktick, usingthe halt signal. For a memory read, this means we have one clock cycle of 20ns to do all thework. For a write, we can also use the clock cycle in which the halt signal is high.

The memory has an access time of 10ns, making the above possible. The address is loaded atthe start of the clockcycle, and the multiplexer controlling the output is set. After 10ns, the databecomes available. The multiplexer output should thus be connected directly to the data output,with no latches or registers in between.

Writing is slightly more complicated. The data has to be setup 8ns before the falling edgeof the signals controlling the write (i.e. the lower/upper signals, the enable signal, and the writeenable signal). The data is written on the falling edge of one of the write controlling signals. Thismeans that two clock cycles are needed to write a byte of data. When halt is high, before thewrite, the setup is done: the lower/upper signals, and the enable signal are directly connected tothe address bits driving them. The memory address is directly connected to the other addressbits. When halt goes low, the write signal is asserted, and the data inputs are connected to theright output. The write is committed at the end of the cycle, when halt goes high again, byde-asserting the write signal. An example write waveform can be seen in figure 3.3.

3.5 Address translator

The address translator translates addresses from the 6502 address space to addresses in the mem-ory address space. With some thought, very little hardware is needed to make this translation.

First, a location in the address space of the 6502 must be chosen where we can place a bank ofexternal memory. The size of the bank is determined by the size of the address space available.In this case, the space from 5000h to CFFFh is unused, so this location is used. The size of theaddress space should be a power of 2, otherwise, not all of the memory can be used. The chosenaddress space results in a bank size of 32kB. The 15 lowest address bits determine which byte wewant to access in the current bank, and can be directly connected to the controller. The MMUshould connect the various signals from the memory controller to the CPU when the address liesin the chosen range.

9

Page 12: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

CLK LLLL�HHHH�LLLL�HHHHHhalt HHHH�LLLLLLLLLL�HHHHwrite enable FFÆHHHHHHHHHHHHHH�FFaddress VV�VVVVVVVVVVVVVV�VVdata in VV�VVVVVVVVVVVVVV�VVmem enable 1 FF LLLLLLLLLLLLLL�FFmem lower 1 FFÆHHHHHHHHHHHHHH�FFmem upper 1 FF LLLLLLLLLLLLLL�FFmem address VV�VVVVVVVVVVVVVV�VVmem wr enable HHHH�LLLLLLLLLL�HHHHmem data 1 ZZZZ�VVVVVVVVVVVVVVVV

Figure 3.3: An example write: an upper byte is written to memory chip 1. For conciseness, onlythe signals for chip 1 are shown. Things to note are that the enable, lower, upper and addresssignals are connected directly to the inputs. The write is controlled by the write enable signal, onthe clock, and the data is written to the outputs on the clock as well.

10

Page 13: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Five address bits remain, these are connected to a register which can be used to switch memorybanks. This register, in turn, is memory mapped to an available address using the (existing) I/Omechanism. See also figure 3.4.

bank address

19 15 14 0

Figure 3.4: Address composition

3.6 Memory test algorithm

When testing the memory, two distinct tests must be performed. First, the addressing mechanismhas to be tested. And secondly, the data cells themselves have to be tested. Of course, the first testis always slightly coupled with the second test, meaning that some data errors are also detectedin the addressing test. The idea for the memory tests were taken from the memtest86 program(see [2]).

3.6.1 Addressing test

The most basic (and very effective) addressing test is writing the address of the memory locationto each location. This ensures that every individual cell is addressed correctly. In our case, it isslightly more complicated, because the addresses are larger than the data width of the memory.The solution is to perform the test in three phases. First, bits 0-7 are written and read back. Then,bits 8-14 are used, and lastly, bits 15-19 are used. This makes sure that all addresses are correct.

3.6.2 Data test

For data testing, the so-called moving inversion algorithm can be used. The basic idea is thatthe memory is filled with a certain pattern. Then, the pattern is read, inverted and written back,starting from the lowest address. When the highest address is reached, the test is done again, butthen starting from the highest address. This is a good way to detect problems due to adjacencyeffects, i.e. writing of one memory location affects another location. Depending on the speed ofthe test, and the time which we want to spend on testing, one or multiple patterns can be used.The first candidate is a pattern of just zeroes. For more testing, a ’walking bit’ pattern sequencecan be used, in which each of the bits is tested, by setting only that bit to one and running thewhole test. This implies that the whole test has to be run eight times in sequence, which couldtake a long time. The pattern sequence used is stored in an array. When a shorter test time isrequired, the sequence can be trimmed.

11

Page 14: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

C H A P T E R 4

Code documentation

4.1 VHDL code

4.1.1 Toplevel

At the toplevel, the only change is in the connections from the MMU to the external memorychips. First, external signals are added to connect to the memory chips:

34 SRAM_ADDR : out std_logic_vector(17 downto 0);

35 SRAM1_CE : out std_logic;

36 SRAM1_LB : out std_logic;

37 SRAM1_UB : out std_logic;

38 SRAM1_IO : inout std_logic_vector(15 downto 0);

39 SRAM2_CE : out std_logic;

40 SRAM2_LB : out std_logic;

41 SRAM2_UB : out std_logic;

42 SRAM2_IO : inout std_logic_vector(15 downto 0);

43 SRAM_OE : out std_logic;

44 SRAM_WE : out std_logic;

And then, these signals are connected to the MMU:

182 SRAM1_CE => SRAM1_CE,

183 SRAM1_LB => SRAM1_LB,

184 SRAM1_UB => SRAM1_UB,

185 SRAM1_IO => SRAM1_IO,

186 SRAM2_CE => SRAM2_CE,

187 SRAM2_LB => SRAM2_LB,

188 SRAM2_UB => SRAM2_UB,

189 SRAM2_IO => SRAM2_IO,

190 SRAM_OE => SRAM_OE,

191 SRAM_WE => SRAM_WE,

192 SRAM_ADDR => SRAM_ADDR,

4.1.2 Memory management unit

The memory management unit was changed to include the memory controller. Data lines tothe external memory chips are added, and connected to the memory controller, and the memorycontroller itself is added to the internal address, data and control lines of the MMU. Also, thepage select register in the I/O controller is connected to the memory controller.

Entity description

The data lines to connect to the external memory chips are added to the entity description of theMMU:

31 SRAM_ADDR : out std_logic_vector(17 downto 0);

32 SRAM1_CE : out std_logic;

33 SRAM1_LB : out std_logic;

34 SRAM1_UB : out std_logic;

35 SRAM1_IO : inout std_logic_vector(15 downto 0);

12

Page 15: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

36 SRAM2_CE : out std_logic;

37 SRAM2_LB : out std_logic;

38 SRAM2_UB : out std_logic;

39 SRAM2_IO : inout std_logic_vector(15 downto 0);

40 SRAM_OE : out std_logic;

41 SRAM_WE : out std_logic;

Internal signals

Internal signals are added to connect the external memory to the data bus, and to the controlsignals:

55 signal data_mem_to_cpu_ext: std_logic_vector(7 downto 0);

64 signal read_flag_ext : std_logic;

72 signal write_flag_ext : std_logic;

And an internal signal is added to connect the page select register in the I/O controller to thememory controller:

75 signal extram_page : std_logic_vector(4 downto 0) := (others => ’0’);

Memory controller component declaration

The memory controller is declared, and connected to the external memory signals, and to theinternal data, address and control lines. The lower 15 bytes of the address are connected to theinternal address bus, and the upper 5 bytes are connected to the page select register:

124 extram: entity memctrl(arch)

125 port map (CLK => CLK,

126 mem_enable_1 => SRAM1_CE,

127 mem_lower_1 => SRAM1_LB,

128 mem_upper_1 => SRAM1_UB,

129 mem_data_1 => SRAM1_IO,

130

131 mem_enable_2 => SRAM2_CE,

132 mem_lower_2 => SRAM2_LB,

133 mem_upper_2 => SRAM2_UB,

134 mem_data_2 => SRAM2_IO,

135

136 mem_out_enable => SRAM_OE,

137 mem_wr_enable => SRAM_WE,

138 mem_address => SRAM_ADDR,

139

140 read_enable => read_flag_ext,

141 write_enable => write_flag_ext,

142 halt => halt,

143 address(14 downto 0) => address_bus(14 downto 0),

144 address(19 downto 15) => extram_page,

145 data_in => data_cpu_to_mem,

146 data_out => data_mem_to_cpu_ext);

I/O controller component declaration

The component declaration of the I/O controller is changed slightly, to accomodate the page selectregister:

174 extram_page => extram_page);

13

Page 16: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Read/write flags, databus

For each 4K aperture in which the external memory is mapped, the read and write flags need tobe set:

205 with address_bus(15 downto 12) select

206 read_flag_ext <= read_flag when "0101", -- 5000h

207 read_flag when "0110",

208 read_flag when "0111",

209 read_flag when "1000",

210 read_flag when "1001",

211 read_flag when "1010",

212 read_flag when "1011",

213 read_flag when "1100", -- C000h

214 ’0’ when others;

243 with address_bus(15 downto 12) select

244 write_flag_ext <= write_flag when "0101", -- 5000h

245 write_flag when "0110",

246 write_flag when "0111",

247 write_flag when "1000",

248 write_flag when "1001",

249 write_flag when "1010",

250 write_flag when "1011",

251 write_flag when "1100", -- C000h

252 ’0’ when others;

Also, the data output of the external memory has to be connected to the central data bus whenone of the apertures in which the external memory is mapped is selected:

259 with address_bus(15 downto 12) select

260 data_mem_to_cpu <= data_mem_to_cpu_0xxx when "0000",

261 data_mem_to_cpu_1xxx when "0001",

262 data_mem_to_cpu_2xxx when "0010",

263 data_mem_to_cpu_3xxx when "0011",

264 data_mem_to_cpu_4xxx when "0100",

265 data_mem_to_cpu_ext when "0101", -- 5000h

266 data_mem_to_cpu_ext when "0110",

267 data_mem_to_cpu_ext when "0111",

268 data_mem_to_cpu_ext when "1000",

269 data_mem_to_cpu_ext when "1001",

270 data_mem_to_cpu_ext when "1010",

271 data_mem_to_cpu_ext when "1011",

272 data_mem_to_cpu_ext when "1100", -- C000h

4.1.3 Memory controller

The memory controller is the central unit in the design. It is located in memctrl.vhd.

1 library ieee;

2

3 use ieee.std_logic_1164.all;

Entity description

All the signals for connecting the controller are defined. The CLK signal must be connected to thecentral clock.

The halt signal must be connected to the central halt signal of the MMU. When this signal islow, data is read from the memory, or the signals for a write are setup. When the signal is high, awrite is committed to memory. Note that a memory write thus needs two clock cycles, one duringwhich the halt signal is low, and one during which the halt signal is high.

14

Page 17: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

5 entity memctrl is

6 port(CLK : in std_logic;

7 halt : in std_logic;

All signals prefixed with mem_ are used to interface with the external memory on the board.

9 mem_enable_1 : out std_logic; -- enable memory chip 1

10 mem_lower_1 : out std_logic; -- enable lower byte mem chip 1

11 mem_upper_1 : out std_logic; -- enable upper byte mem chip 1

12 mem_data_1 : inout std_logic_vector(15 downto 0);

13 mem_enable_2 : out std_logic; -- enable memory chip 2

14 mem_lower_2 : out std_logic; -- enable lower byte mem chip 2

15 mem_upper_2 : out std_logic; -- enable upper byte mem chip 2

16 mem_data_2 : inout std_logic_vector(15 downto 0);

17 mem_out_enable : out std_logic; -- output enable

18 mem_wr_enable : out std_logic; -- write enable

19 mem_address : out std_logic_vector(17 downto 0);

The last block of signals are used to interface with the controller.

21 read_enable : in std_logic; -- read memory location

22 write_enable : in std_logic; -- write memory location

23 address : in std_logic_vector(19 downto 0);

24 data_in : in std_logic_vector(7 downto 0);

25 data_out : out std_logic_vector(7 downto 0)

26 );

27 end entity memctrl;

Internal signals

The chipselect and byteselect signals are used to select the correct chip, and either the loweror the upper byte of that chip, for a certain memory address.

29 architecture arch of memctrl is

30 signal chipselect, byteselect : std_logic;

Memory chip control signals

The outputs of the ram chips are always enabled (line 33). The chipselect and byteselectsignals are used to drive the signals for enabling/disabling the lower and upper bytes for bothchips (lines 35–38 and lines 39–40), and for enabling the chips themselves.

31 begin

32

33 mem_out_enable <= ’0’;

34

35 mem_lower_1 <= ’0’ when chipselect = ’0’ and byteselect = ’0’ else ’1’;

36 mem_upper_1 <= ’0’ when chipselect = ’0’ and byteselect = ’1’ else ’1’;

37 mem_lower_2 <= ’0’ when chipselect = ’1’ and byteselect = ’0’ else ’1’;

38 mem_upper_2 <= ’0’ when chipselect = ’1’ and byteselect = ’1’ else ’1’;

39 mem_enable_1 <= ’0’ when chipselect = ’0’ else ’1’;

40 mem_enable_2 <= ’0’ when chipselect = ’1’ else ’1’;

The lower bit of the address is used to select either the upper or the lower byte (line 44). Theupper bit is used to select either the first or the second memory chip (line 43). The rest of theaddress bits are used to drive the address lines of the memory chips (line 42).

42 mem_address <= address(18 downto 1);

43 chipselect <= address(19);

44 byteselect <= address(0);

15

Page 18: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Write signal

The write signal is inverted and passed through to the memory chips when the controller is notin the halt cycle. When the controller is in the halt cycle, the external write signal is always high.This makes sure that there is always a rising edge at the end of a write on one of the controlsignals, triggering the memory chip to write the data. See also figure 3.3 on page 10, and theaccompanying discussion on timing on page 9.

45 process (CLK)

46 begin

47 if rising_edge(CLK) then

48 if halt = ’0’ then

49 mem_wr_enable <= not write_enable;

50 else

51 mem_wr_enable <= ’1’;

52 end if;

53 end if;

54 end process;

Input data multiplexing

The input data is multiplexed on the rising edges of the clock, when the controller is not halted.By default, the outputs are switched to high-impedance mode. Only the 8 bits selected by thecombination of the lower and upper bit of the address are connected to the data inputs.

56 process (CLK)

57 begin

58 if rising_edge(CLK) and halt = ’0’ then

59 mem_data_1 <= (others => ’Z’);

60 mem_data_2 <= (others => ’Z’);

61 if write_enable = ’1’ then

62 if chipselect = ’0’ then

63 if byteselect = ’0’ then

64 mem_data_1(7 downto 0) <= data_in;

65 else

66 mem_data_1(15 downto 8) <= data_in;

67 end if;

68 else

69 if byteselect = ’0’ then

70 mem_data_2(7 downto 0) <= data_in;

71 else

72 mem_data_2(15 downto 8) <= data_in;

73 end if;

74 end if;

75 end if;

76 end if;

77 end process;

Output data multiplexing

Finally, the correct data bytes are connected to the data output, using the chipselect andbyteselect signals. The selected_chip signal is used to make the notation more concise.

79 data_out_mux: block

80 signal selected_chip : std_logic_vector(15 downto 0);

81 begin

82 selected_chip <= mem_data_1 when chipselect = ’0’

83 else mem_data_2;

84 data_out <= selected_chip(7 downto 0) when byteselect = ’0’

85 else selected_chip(15 downto 8);

16

Page 19: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

86 end block;

87

88 end architecture arch;

4.1.4 I/O controller

A register has been added to the I/O controller to select the page of the external memory which ismapped to the memory space of the CPU.

Entity description

One signal is added, to set the page of the external memory:

35 extram_page : out std_logic_vector(4 downto 0)

Reading/writing the register

The register is placed at address 500h in the address space of the I/O controller. When this addressis selected, the contents of the register are read or written, according to the read/write flags:

292 when x"500" => sig_extram_page <= data_in(4 downto 0);

445 when x"500" => data_out <= "000" & sig_extram_page;

4.2 Memory test program

The memory test program is fairly simple, and consists of one C file, memtest.c. The programcan be compiled with the supplied Makefile and uploaded to the CPU. When executed, it runsthe test program once.

The file starts with the standard includes:

1 #include <peekpoke.h>

2 #include <string.h>

3 #include <stdio.h>

4 #include <stdlib.h>

5

6 #include "fpga.h"

Three macros are defined, to execute a memory test function on each memory page. The macrosare used to keep the code more readable. Initially, functions were used, with a function pointeras argument. Unfortunately, for some reason, this did not work on the 6502, buggy behaviourwas encountered.

The first macro is used to call the address test functions. These need the current page asargument. A message is passed to the macro to display status information. The message mustcontain a %02x format specifier, to display the current page.

8 #define run_addr_check(function, message) {\

9 for(page = 0; page <= 0x1f; page++) { \

10 POKE(0xe500, page); \

11 sprintf(s, message, page); \

12 fpga_puts(s); \

13 function(page); \

14 } \

15 }

The second macro is used to call the functions for the running inverse test algorithm. Thesefunctions need the current pattern as an argument. The status message contains 2 %d formatspecifiers for displaying the number of the current pattern, and the total number of patterns, anda %02x format specifier, to display the current page.

17

Page 20: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

17 #define run_inv(function, pattern, message) {\

18 for(page = 0; page <= 0x1f; page++) { \

19 POKE(0xe500, page); \

20 sprintf(s, message, pattern+1, N_PATTERNS, page); \

21 fpga_puts(s); \

22 function(patterns[pattern]); \

23 } \

24 }

And lastly, a macro is defined to call a running inverse test function, accessing the pages inreverse order. This is needed for the second inverting pass of the algorithm. The arguments ofthe macro are the same as those of the run_invmacro.

26 #define run_inv_reverse(function, pattern, message) {\

27 for(page = 0x1f; page <=0x1f; page--) { \

28 POKE(0xe500, page); \

29 sprintf(s, message, pattern+1, N_PATTERNS, page); \

30 fpga_puts(s); \

31 function(patterns[pattern]); \

32 } \

33 }

Function declarations are included for all test functions in the program.

35 void addr_write_1(unsigned char page);

36 void addr_check_1(unsigned char page);

37 void addr_write_2(unsigned char page);

38 void addr_check_2(unsigned char page);

39 void addr_write_3(unsigned char page);

40 void addr_check_3(unsigned char page);

41 void mov_inv_write(unsigned char pattern);

42 void mov_inv1(unsigned char pattern);

43 void mov_inv2(unsigned char pattern);

44 void mov_inv3(unsigned char pattern);

The variable s serves as a global buffer for storing formatted output messages, which can thenbe output to the serial port using fpga_puts.

46 char s[100];

The array patterns contains the patterns used in the moving inverse test. It includes the zeropattern, eight patterns with a single bit set to one in each of the eight possible positions, and twopatterns with alternating ones and zeroes. N_PATTERNS is set to the total number of patterns.

47 #define N_PATTERNS 11

48 char patterns[] = { 0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0xaa, 0x55 };

The main function calls each of the tests in sequence. First, all the address check tests are run.Then, for each available pattern, the moving inversion tests are run.

50 int main() {

51 unsigned char page;

52 int i;

53

54 run_addr_check(addr_write_1, "Check 1 write, page 0x%02x of 0x1f\r\n");

55 run_addr_check(addr_check_1, "Check 1 read, page 0x%02x of 0x1f\r\n");

56 run_addr_check(addr_write_2, "Check 2 write, page 0x%02x of 0x1f\r\n");

57 run_addr_check(addr_check_2, "Check 2 read, page 0x%02x of 0x1f\r\n");

58 run_addr_check(addr_write_3, "Check 3 write, page 0x%02x of 0x1f\r\n");

59 run_addr_check(addr_check_3, "Check 3 read, page 0x%02x of 0x1f\r\n");

60

61 for(i=0; i<N_PATTERNS; i++) {

62 run_inv(mov_inv_write, i, "Moving inversion, %d of %d, writing page 0x%02x of 0

x1f.\r\n");

63 run_inv(mov_inv1, i, "Moving inversion, %d of %d, inversion #1, page 0x%02x of 0

x1f.\r\n");

18

Page 21: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

64 run_inv_reverse(mov_inv2, i, "Moving inversion, %d of %d, inversion #2, page 0x

%02x of 0x1f.\r\n");

65 run_inv(mov_inv3, i, "Moving inversion, %d of %d, inversion #3, page 0x%02x of 0

x1f.\r\n");

66 }

67 exit(0);

68 }

The first address write and check functions write, respectively check, the lower eight bits ofthe memory address, for each location in the page.

70 void addr_write_1(unsigned char page) {

71 unsigned int mem;

72 for(mem = 0x5000; mem < 0xd000; mem++) {

73 POKE(mem, mem);

74 }

75 }

76

77 void addr_check_1(unsigned char page) {

78 unsigned int mem;

79 unsigned char val;

80

81 for(mem = 0x5000; mem < 0xd000; mem++) {

82 val = PEEK(mem);

83 if(val != mem & 0x00ff) {

84 sprintf(s, "Mismatch at addr 0x%x.\r\n", mem);

85 fpga_puts(s);

86 }

87 }

88 }

The second address write and check functions write, respectively check, bits 9 thru 15 of thememory address, for each location in the page.

90 void addr_write_2(unsigned char page) {

91 unsigned int mem;

92 for(mem = 0x5000; mem < 0xd000; mem++) {

93 POKE(mem, (mem >> 8));

94 }

95 }

96

97 void addr_check_2(unsigned char page) {

98 unsigned int mem;

99 unsigned char val;

100

101 for(mem = 0x5000; mem < 0xd000; mem++) {

102 val = PEEK(mem);

103 if(val != (mem >> 8)) {

104 sprintf(s, "Mismatch at addr 0x%04x: expecting 0x%02x, got 0x%02x.\r\n", mem,

mem >> 8, val);

105 fpga_puts(s);

106 }

107 }

108 }

And the third address write and check functions write, respectively check, the current pagenumber, for each location in the page.

110 void addr_write_3(unsigned char page) {

111 unsigned int mem;

112 for(mem = 0x5000; mem < 0xd000; mem++) {

113 POKE(mem, page);

114 }

19

Page 22: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

115 }

116

117 void addr_check_3(unsigned char page) {

118 unsigned int mem;

119 unsigned char val;

120

121 for(mem = 0x5000; mem < 0xd000; mem++) {

122 val = PEEK(mem);

123 if(val != page) {

124 sprintf(s, "Mismatch at addr 0x%04x: expecting 0x%02x, got 0x%02x.\r\n", mem,

page, val);

125 fpga_puts(s);

126 }

127 }

128 }

The setup function of the moving inverse algorithm writes the current pattern to each memorylocation.

130 void mov_inv_write(unsigned char pattern) {

131 unsigned int mem;

132 for(mem = 0x5000; mem < 0xd000; mem++) {

133 POKE(mem, pattern);

134 }

135 }

The first function of the moving inverse algorithm reads and checks the pattern from memory,and writes back the inverse of the pattern.

137 void mov_inv1(unsigned char pattern) {

138 unsigned int mem;

139 unsigned char val, inv;

140 inv = ˜pattern;

141 for(mem = 0x5000; mem < 0xd000; mem++) {

142 val = PEEK(mem);

143 if(val != pattern) {

144 sprintf(s, "Mismatch at addr 0x%04x: expecting 0x%02x, got 0x%02x.\r\n",

pattern, val);

145 fpga_puts(s);

146 }

147 POKE(mem, inv);

148 }

149 }

The second function of the moving inverse algorithm, which is executed from the highestlocation downwards, reads and checks the inverted pattern, and writes back the original pattern.

151 void mov_inv2(unsigned char pattern) {

152 unsigned int mem;

153 unsigned char val, inv;

154 inv = ˜pattern;

155 for(mem = 0xcfff; mem >= 0x5000; mem--) {

156 val = PEEK(mem);

157 if(val != inv) {

158 sprintf(s, "Mismatch at addr 0x%04x: expecting 0x%02x, got 0x%02x.\r\n", inv,

val);

159 }

160 POKE(mem, pattern);

161 }

162 }

The last function of the moving inverse algorithm reads and checks the original pattern,written by the second function.

20

Page 23: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

164 void mov_inv3(unsigned char pattern) {

165 unsigned int mem;

166 unsigned char val;

167 for(mem = 0x5000; mem < 0xd000; mem++) {

168 val = PEEK(mem);

169 if(val != pattern) {

170 sprintf(s, "Mismatch at addr 0x%04x: expecting 0x%02x, got 0x%02x.\r\n",

pattern, val);

171 }

172 }

173 }

21

Page 24: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

C H A P T E R 5

Conclusion and recommendations

The memory controller which was developed fullfills the requirements set for the project. It givesa significant increase in available memory, using the external RAM on the development board.The controller is developed as a separate unit, and is reusable in a similar microcontroller. The testprogram also fullfills its requirements; during development several errors in the implementationof the memory controller were discovered and corrrected using the program.

The speed of the memory controller is such that no extra wait cycles have to be introduced.Theoretically, the speed could be improved slightly, so that writes and reads both only takeone clocktick. In practice this might prove to be difficult, and in this case, it was not needed.The reusability of the controller is somewhat impeded by the interface dictated by the existingCPU. There is no real solution for this problem, and it is questionable if reusability should be animportant goal when developing these kind of components.

The structure of the development process has some room for improvement, there was notmuch direction from the side of the client in terms of development process. The cause of thisseems to be the conflicting interests of the client and the coordinator; the client was focused mainlyon the end-product, whereas the main interest of the coordinator was in a proper developmentprocess.

22

Page 25: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

A P P E N D I X A

Working with the FPGA under Linux

As I developed and tested the FPGA software under Linux, I have added some notes on how toget things working. Hopefully, they may be of use to someone.

A.1 USB to Serial Converter

The 6502 softcore uses the RS-232 serial port on the FPGA board to connect to a host computer.Less and less computers, especially laptop computers, still have such a port, however. As aworkaround, a USB to serial converter can be used. Getting the USB to serial converters whichare used at the Embedded Software Lab to work under Linux is fairly simple, assuming a 2.6kernel is used. The following option needs to be enabled:

Symbol: USB_SERIAL_PL2303 [=m]

Prompt: USB Prolific 2303 Single Port Serial Driver

Defined at drivers/usb/serial/Kconfig:406

Depends on: USB!=n && USB_SERIAL

Location:

-> Device Drivers

-> USB support

-> USB Serial Converter support

-> USB Serial Converter support (USB_SERIAL [=m])

Compile the new kernel, and install the modules in the usual way, and after rebooting the USBsubsystem should automatically insert this module when the cable is plugged into a USB port.The port can be accessed under /dev/ttyUSB0. For this to work fully automatically, the hotplugsubsystem should be working. In most modern Linux distributions, this should already be thecase.

A.2 Xilinx software

Installing the Xilinx webpack is relatively straightforward. For this project, an old version wasused (7.1i), the newer version didn’t work out of the box with the existing VHDL code for the6502 soft core. For the (graphical) installer to work, the X server has to accept connections overTCP/IP, so make sure the -nolisten tcp option is not enabled for your X server. When the Xserver doesn’t accept TCP/IP connections, the installer will lock up for a few minutes and thenexit with a very unclear error message. The rest of the installation is straightforward. At the end,some error messages are displayed about the kernel modules, which can be ignored.

Getting the kernel module to work (windrvr6, which is needed for programming the FPGAusing the impact tool) takes some handwork. Xilinx only provides a version for the 2.4 kernelseries, the 2.6 kernel series use a very different module format. The link on their site to thesources of the 2.6 module was dead, unfortunately, at the time this document was written. Fromhttp://www.jungo.com/download/WD702LN.tgz, the sources can be downloaded. Un-tar them,and run make in the WinDriver/redist directory. Install the module using make install, andthen insert it using modprobe. Additionally, the modules parport, parport_pc and lp are neededfor impact to program the FPGA.

23

Page 26: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

When using udev for device management, the device inode for the windrvr6 driver is notautomatically created. Add mknod /dev/windrvr6 c 253 0 to your startup scripts to take careof this. And, of course, all the permissions on the inodes should be correct for the whole thing towork.

24

Page 27: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

A P P E N D I X B

External memory timing diagrams

The two timing diagrams below correspond to the read and write cycles used to access the externalmemory. They are copied from [1].

DATA VALID

READ1.eps

PREVIOUS DATA VALID

t AA

t OHAt OHA

t R C

DOUT

ADDRESS

Figure B.1: Read cycle 1

DATA UNDEFINED

t WC

ADDRESS 1 ADDRESS 2

t WC

HIGH-Z

t PBW

WORD 1

LOW

WORD 2

UB_CEWR4.eps

t HD

t SA

t HZWE

ADDRESS

CE

UB, LB

WE

DOUT

DIN

OE

DATAINVALID

t LZWE

t SD

t PBW

DATAINVALID

t SDt HD

t SA

t HA t HA

Figure B.2: Write cycle 4

25

Page 28: Memory controller for a 6502 CPU in VHDL - st.ewi.tudelft.nl · able is the VHDL source code for the memory management unit. It should consist of a general entity which communicates

Bibliography

[1] Integrated Silicon Solution, Inc. IS61LV25616AL data sheet. http://www.issi.com/pdf/61LV25616AL.pdf, February 2006.

[2] Memtest86 website. http://www.memtest86.com/.

[3] Xilinx ISE WebPACK design software. http://www.xilinx.com/ise/logic design prod/webpack.htm.

26