Skip to content
This repository has been archived by the owner on Jul 23, 2021. It is now read-only.

aaa. Welcome to fpgasm!

stacksmith edited this page Sep 30, 2012 · 10 revisions

You can't wait to build bare-metal FPGA circuits, so

fpgasm in a nutshell

This is a basic tool thats allows you to define and instantiate circuits called modules. Each module contains instances of other modules, along with location and Wiring information. Eventually, your 'top' module solves your problem. That's pretty much it - no expressions, no logical operators, no loops.

Like chess, the moves are few and simple. The game has infinite possibilities.

Each module exposes "input" and "output" pins (that later will be wired up). And there is a way to parametrize modules so that different instances of, say, a LUT, can initialize it with different values to create your logic.

Here is a simple example of a module. This module defines 4 pushbuttons on my Digilent Spartan S3 board:

	//================================    
	// Digilent Spartan S3 Demo board
	// 4 pushbuttons
	//================================
	Buttons() output( OUT[4] /*Bus of 4 wires*/) {
	  button0 InSimple loc:M13; //M13 is the FPGA pin - no ucf files needed.
	  wire button0's OUT to my OUT[0] ; //We know that InSimple declares an OUT pin
	  button1 InSimple loc:M14;
	  wire button1 OUT to my OUT[1] ;
	  button2 InSimple loc:L13;
	  wire his OUT to my OUT[2] ; //his refers to button2
	  button3 InSimple loc:L14;
	  wire his OUT to my OUT[3] ; //now his refers to button3
	}

Note the wiring syntax. Module InSimple, a simple input port, declares a pin OUT. So we literally wire the instance's OUT pin to our own output pin. You can optionally use the posessive 's to make your code more readable. The module's own pins are called my or our. His (or her) refers to the last instance declared, for convenience.

loc:M13 is a simple name:value asignment for the instance. In this case, we are setting the instance's location. Now, we can use the module Buttons as an instance inside another module:

//
Top() {
  buttons Buttons;
}

Imagine module similar to Buttons called Leds, with 8 LEDs, exposing a bus of four input pins. We can wire the two together up like this:

//
Top() {
  button Buttons;
  led Leds;
  wire button OUT[0:3] to led IN[0:3];   //'s are optional
  wire my gnd to led[4:7]; //inputs need to be connected, my gnd is GROUND
}

This is an actual circuit that lights leds when buttons are pressed.

Now let me show you how to use parameters. Let's revisit the Buttons module definition, and set it up to allow different IO standards, such as LVTTL or LVCMOS33.

//================================    
// Digilent Spartan S3 Demo board
// 4 pushbuttons
//================================
Buttons() output(OUT[4] ) {
  button0 InSimple loc:M13 std:LVTTL; //InSimple exposes parameter 'std'
  wire his OUT to my OUT[0] ;
  button1 InSimple loc:M14 std:LVTTL;
  wire his OUT to my OUT[1] ;
  button2 InSimple loc:L13 std:LVTTL;
  wire his OUT to my OUT[2] ;
  button3 InSimple loc:L14 std:LVTTL;
  wire his OUT to my OUT[3] ; 
}

Now the buttons will be instantiated to support LVTTL io standard. To make it more generic, we can parametrize the values of std like this:

//================================    
// Digilent Spartan S3 Demo board
// 4 pushbuttons
//================================
Buttons(level) output(OUT[4] ) {
  button0 InSimple loc:M13 std:!level!; //pass on our level to InSimple's std
  wire his OUT to my OUT[0] ;
  button1 InSimple loc:M14 std:!level!;
  wire his OUT to my OUT[1] ;
  button2 InSimple loc:L13 std:!level!;
  wire his OUT to my OUT[2] ;
  button3 InSimple loc:L14 std:!level!;
  wire his OUT to my OUT[3] ; 
}

Now we will need to set level parameter when instantiating Buttons like this:

//
Top() {
  button Buttons level:LVTTL; //pass on LVTTL to Buttons' level
  led Leds;
  wire button's OUT[0:3] to led's IN[0:3];
}

Not setting level is considered an error. To make it optional, we can introduce a default value in the Buttons definition like this:

//
Buttons(level:LVTTL) output:(OUT[4] ) {
  button0 InSimple loc:M13 std:!level!;
  ...

Now we can explicitly set the level or default to LVTTL without setting it. (InSimple does this with std which is why we didn't have to set it in the first example).

//
  button Buttons level:LVCMOS33;
    or
  button Buttons;  //defaults to LVTTL

Now you know pretty much all the "chess moves". To learn to play, follow some examples in fpgasm-test repo.