ECAD & Architecture Workshop Six

Interfacing the Altera FLEX chip to the ARM


workshop home | workshops: 1 | 2 | 3 | 4 | 5 | 6

Time to learn a bit about hardware/software codesign! In this workshop you will writing both a small program for the ARM, as well as reusing and modifying some of the Verilog you have written in previous workshops. The work is split into two tasks to help you build up the hardware and software in stages.

Task 1

Your first task is to interface the ARM to the 7-segment LEDs controlled by the FLEX. This will be used in Task 2 to display mouse data, but for this task you could just display an incrementing number. The FLEX chip has been allocated a chunk of the ARM's address space and so it needs to act like an memory device to allow the ARM to write data to it. In Task 2 the FLEX will also allow the ARM to read data from it.

You need to write some Verilog code for the FLEX to latch data written by the ARM, decode the data (using the HexToLEDs module from previous workshops) and output the hex digits on the LEDs.

To make the FLEX respond to write requests from the ARM, you will need to:

Pin definitions for the FLEX chip are in lab6.acf. To get you started with your Verilog code, below is a suitable header including the mouse interface and data output bits needed for Task 2 (which you can ignore for now) and the tristate buffer for nWAIT mentioned above:

module lab6(ledA, ledB, mdata, mclk, d, a, XCLK, nCE2, nOE, nWE, INT4, nWAIT);

  output [7:0] ledA, ledB;           // 8-bit outputs to LEDs
  inout  mdata, mclk;                // mouse serial interface
  inout  [7:0] d;                    // bidirectional data bus
  input  [7:0] a;                    // address bus
  input  XCLK, nCE2, nOE, nWE;       // clock and memory bus control
  output INT4;                       // interrupt 4 line
  inout  nWAIT;                      // bidirectional memory timing control

  TRI    tbuf_for_nWAIT(1,0,nWAIT);  // make nWAIT tristate
  wire   [7:0] data_out=0;           // zero data output to ARM for now
  wire   enable_data_out=0;          // don't output data to ARM for now
  TRI    tbuf_for_D0(data_out[0],enable_data_out,d[0]);
  TRI    tbuf_for_D1(data_out[1],enable_data_out,d[1]);
  TRI    tbuf_for_D2(data_out[2],enable_data_out,d[2]);
  TRI    tbuf_for_D3(data_out[3],enable_data_out,d[3]);
  TRI    tbuf_for_D4(data_out[4],enable_data_out,d[4]);
  TRI    tbuf_for_D5(data_out[5],enable_data_out,d[5]);
  TRI    tbuf_for_D6(data_out[6],enable_data_out,d[6]);
  TRI    tbuf_for_D7(data_out[7],enable_data_out,d[7]);
  assign INT4=0;                     // disable interrupts for now
  assign mdata=1'bz;                 // don't use mouse data for now (tristate)
  assign mclk=1'bz;                  // don't use mouse clock for now (tristate)
...

Write an accompanying piece of ARM assembler to write values to the LEDs. The ARM to FLEX data bus is just 8-bits wide so use the strb instruction to store a byte to the FLEX since a str instruction will result in 4 bytes being written. The FLEX chip is memory mapped onto the ARMs memory map over the area 0x08000000 to 0x0bffffff. Since the FLEX is only fed the first 8 address lines, only addresses 0x08000000 to 0x080000ff are useful, the higher addresses mapping onto the lower ones (e.g. 0x08000103 looks the same as 0x08000003 to the FLEX).

For more detail on the interface between the FLEX and the ARM, see memo 21.

Task 2

The task for this workshop is to interface the mouse to the ARM in such a way that pressing either mouse button causes the ARM to print the current "position" of the mouse in the debugger. The position should be an X,Y pair where X and Y are both signed 16 bit numbers. The lower 8 bits of the X value should be displayed on the 7-segment LEDs when a mouse button is pressed. If both mouse buttons are pressed simultaneously then the program should terminate.

This task should build on task 1, i.e. the ARM should write the value to be dispalyed to the FLEX and therefore if the ARM is kept in a reset state the display should not be updated.

To do this you will need:

The program should only print the X and Y values when the buttons are first pressed, not while they are held down.

To allow the ARM to read data from the FLEX, you will need to:
	assign enable_data_out = !nOE && (your equations here);

Note that the ARM is configured to treat this bus as 8 bits wide so if you use ldr to load a 32 bit word the ARM will to four 8 bit reads on your behalf.

Hint: to multiplex a set of buses together you can use the `?' operator multiple times, e.g.:

        assign data_out = (select_bus == 2'd0) ? bus0 :
                          (select_bus == 2'd1) ? bus1 :
                          (select_bus == 2'd2) ? bus2 : bus3;

Bonus Task

The bonus task is optional, but if you have time it would be a good idea to tackle it because you will learn more about interrupts and interrupt handlers.

When running code from Task 2 it is possible for events to lost if you hit mouse buttons too quickly (particularly if you reconfigure the remote debugging to use 9600 baud rather than 38400 baud). This is due to the ARM being busy sending output to the console over the serial line which prevents your polling code from checking for new mouse data. The solution we will adopt here is to write an interrupt handler to buffer data sent by the FLEX so that nothing is lost (unless the buffer overflows!). The FLEX should interrupt the ARM whenever new mouse data is available, and the ARM should buffer mouse events by placing them in a circular FIFO. A user mode routine should check the FIFO and print out data as before (i.e. every time a mouse button down event is noticed). Since interrupts can still be handled whilst data is being sent to the console, mouse events should be safely buffered and none should be lost.

Before writing the interrupt handler to implement the buffer, try writing a simpler interrupt handler to just display the mouse buttons on the ARM's four status LEDs. The LEDs can be written to by storing a byte, containing the appropriate bits in the top nibble, to address: 0xffff1c08

The Angle debugger (the code that sits on the ARM board and communicates with the debugger code running on the PC) uses the normal interrupt line (INT) but the fast interrupt line (FIQ) is available for our use. FIQ has the advantage that extra registers are available (r8-r13) so for simple routines (like this one) there is no need to save any state to free up registers. Just don't use (r0-r7) because they are shared with the user mode code so would have to be saved before use.

The SHARP LH77790B chip contains an interrupt controller as well as an ARM core and other peripherals. The FLEX chip is connected to the interrupt controller via external interrupt line 4 (INT4 on the FLEX, CH4 on the interrupt controller). The following routines enable and disable FIQs from the FLEX.

	;; turn on external interrupt 4 line (ch4 to interrupt controller)
	;; enter with r0=address of your interrupt handler
int4on
	;; make FIQ vector point to your interrupt handler
	mov	r1,#0x3c
	str	r0,[r1]

	;; tell FLEX chip to cancel interrupt
	ldr	r0,[pc,#(FLEXintCtl-8-.)]	; get FLEX base address
	ldrb	r0,[r0]				; clear interrupt flag on FLEX

	;; configure the interrupt controller
	ldr	r0,[pc,#(intControllerRegs-8-.)] ; get base address of interrupt controller
	mov	r1,#0x55	; set all external inputs to active high
	orr	r1,r1,r1,LSL#8	; r1=0x5555
	str	r1,[r0]         ; write ICR0 (interrupt control register 0)

	mov	r1,#0x010	; enable just ch4
	str	r1,[r0,#0x10]	; write FIQER (FIQ enable register)

	mov	pc,lr		; return from subroutine


	;; turn off external interrupt 4 line (ch4 to interrupt controller)
int4off
	ldr	r0,[pc,#(intControllerRegs-8-.)]
	mov	r1,#0		; clear all FIQ enables
	str	r1,[r0,#0x10]	; write FIQER

	mov	pc,lr		; return from subroutine


intControllerRegs
	DCD	0xffffa800	; start of interrupt controller registers
FLEXadr
	DCD	0x08000000
FLEXintCtl
	DCD	0x08000010      ; address for clearing the FLEX interrupt

The int4on routine sets up the FIQ interrupt vector to point to a routine whose address you pass in r0. FIQs are turned off with int4off. Obviously you need to write the interrupt handler.

Return from an interrupt requires:

        subs pc,lr,#4

4 has to be subtracted from the lr because the pc is incremented before the interrupt is taken, so the lr didn't receive the desired value. The "s" flag is required because in this particular circumstance it causes the mode bits to be swapped from FIQ mode to user mode.

Notice that in int4on there is a section of code which tells the FLEX chip to clear interrupts. Your interrupt handler must also clear the interrupt signal from the FLEX chip.

You need to add some Verilog to your design for Task 2 to control interrupts. When new data has been received from the mouse you need to raise the INT4 output. Your ARM code then clears this signal when it enters the interrupt handler, e.g. by reading from address 0x08000010 (which is the address used in the code above). So your Verilog code needs to decode this address and clear the INT4 signal as appropriate.

Once you've finished - congratulations! You've designed a device and written a device driver.

Ticking Criteria

  1. The verilog and ARM code for tast 2 (but not task 1 as task 2 builds on task 1) must be commented.
  2. You must give a live demonstration of your solution to task 2.
  3. The verilog code must be strictly synchronous with all clocks coming from one of the two clock distribution networks. Derived clocks which are distributed over programmable wiring are dangerous because clock skew can vary widly.
  4. The following header must be added to all code submitted:
    //////////////////////////////////////////////////////////////////////////////
    // ECAD+Arch Workshop 6
    //
    // Your name
    // Your college
    // date
    //////////////////////////////////////////////////////////////////////////////