non-blocking assignment in systemc

Verilog uses non-blocking assignment(NBA) to model registers. The basic functionality of non-blocking assignment is, The newly assigned value won’t update until the next clock cycle. Thus If we access(or read) the same variable in the same block, in the same clock cycle after assignment, The value obtained will be the old value. Non-blocking assignment is like effectively assigning value of a signal for next clock cycle, which means signal is updated minimum 1 delta cycle later.

Native C++ data type assignments are sequential and blocking. SystemC provides sc_signal to imitate VHDL’s signal or Verilog’s reg type with Non-Blocking assignment. systemC’s sc_signal uses Evaluate-Update cycle to model Nonblocking assignments.

Let us consider a simple 2 input and gate example,

SC_MODULE (and2) {

  sc_in a;
  sc_in b;
  sc_out c;
  sc_out d;

  void and2_do()
  {
      c = a & b;	// instead use c.write(a.read() & b.read());
      d = c;		// instead use d.write(c.read());
  }

  SC_CTOR (and2) {
    SC_METHOD(and2_do);
      sensitive << a << b;
  }
};

int sc_main(int argc, char* argv[]) {
  and2 DUT("and_DUT");

  sc_trace_file *wf = sc_create_vcd_trace_file("Debug/wvform");
  sc_trace(wf, DUT.a, "a");
  sc_trace(wf, DUT.b, "b");
  sc_trace(wf, DUT.c, "c");
  sc_trace(wf, DUT.d, "d");

  sc_signal a, b, c, d;
  DUT.a(a);
  DUT.b(b);
  DUT.c(c);
  DUT.d(d);

  sc_start(0, SC_PS);

  a = 0;  b = 0;
  sc_start(1, SC_PS);

  a = 1;  b = 0;
  sc_start(1, SC_PS);

  a = 1;  b = 1;
  sc_start(1, SC_PS);

  a = 0;  b = 1;
  sc_start(1, SC_PS);

  a = 0;  b = 0;
  sc_start(1, SC_PS);

  a = 1;  b = 0;
  sc_start(1, SC_PS);
  sc_close_vcd_trace_file(wf);
  return(0);
}

We would expect c and d to contain same value but since c won’t be updated immediately,

sc_signal non-blocking assignment Output
It is clear from the above discussion that sc_signal cannot be used to model combinational logic. Let us look in the next post how to model combinational logic in systemC.

Integrating TCL command prompt in C/C++

TCL command prompt can be integrated into C/C++ programs using tcl/tk libraries. To install these libraries in ubuntu,

apt-get install tcl8.5-dev tk8.5-dev

This small piece of code integrates tcl command prompt and calls a tcl script,
tclc.c

#include <tcl.h>
#include <tk.h>

int AppInit(Tcl_Interp *interp);

int main(int argc, char *argv[]) {
Tk_Main(argc, argv, AppInit);
return 0;
}

int AppInit(Tcl_Interp *interp) {
if (Tcl_Init(interp) != TCL_OK) {
return TCL_ERROR;
}

if (Tk_Init(interp) != TCL_OK) {
return TCL_ERROR;
}

if (Tcl_EvalFile(interp, "startup.tcl") != TCL_OK) {
return TCL_ERROR;
}

return TCL_OK;
}

To compile this application,

gcc tclc.c -o tclc.out -I/usr/include/tcl8.4 -L/usr/lib/ -ltcl8.4 -ltk8.4

Save the following file in the same directory as tclc.out and execute tclc.out,

startup.tcl

set x 4
puts "value of x is $x"

compiling systemc TLM examples

SystemC TLM is available for download from here. To compile TLM examples, you must have systemC installed. Install systemC libraries by referring this post. Extract the TLM archive you downloaded and set $TLM_HOME pointing to top-level directory where you extracted TLM archive. Add these lines to ~/.bashrc,

export SYSTEMC_HOME=/usr/local/systemc-2.2/
export TLM_HOME=/home/sairam/Public/TLM-2009-07-15

Replace the above paths with where you installed your systemc and TLM respectively. In TLM directory, navigate to examples/tlm/build-unix/ and comment out the following line in Makefile.config,

FLAG_WERROR = -Werror

From build-unix directory, in the terminal, execute the following to compile and run TLM examples,

make
make run

Eclipse as development environment(IDE) for systemc in ubuntu

In a previous post, I discussed about how to install systemc, compile and test a sample program. It was a pain creating makefiles and tying every keyword in the program ourselves. Eclipse makes our lives pretty easier with its CDT plug-in. If you are using ubuntu install eclipse through apt,

apt-get install eclipse

(Note: I am assuming your eclipse version is helios and you have installed systemC in /usr/local/systemc-2.2/ directory.)

Now, run eclipse. We have to install CDT plug-in to let eclipse manage C/C++ projects. Click on Help -> Install New Software. In the Install window that appears, paste the following URL in Work With and press enter key,


http://download.eclipse.org/tools/cdt/releases/helios

In the packages that appear, select C/C++ Development Tools and C/C++ Development Tools SDK.

Install CDT

Click Next and from here on follow the wizard and finish installing CDT.

Create a new C/C++ project using the menu File -> New -> Other. Add all your systemc program files to the project. It is time to inform eclipse+CDT where our systemC include headers and library files are. Click Project -> Properties. Navigate to C/C++ Build -> Settings -> Tool Settings tab. Under GCC C++ Compiler -> Includes, Add /usr/local/systemc-2.2/include to Include Paths.

Under GCC C++ Linker -> Libraries, Add /usr/local/systemc-2.2/lib-linux to Library Search Paths. Add systemc and m to Libraries.

Compile and run your project now!

cross compiling for ARM – the LLVM way

I have never used a compiler other than gcc before to cross-compile c code for ARM. This is the first time I am straying outside gcc. All these days I have always heard all kinds of promising reviews on LLVM. So Let us try it out now. If you are using ubuntu, you can install llvm using apt,

apt-get install llvm clang

If you are using any other distribution, follow these instructions to compile llvm from source.

Its time to get our hands dirty. Here is the simple code we are going to compile,

#include<stdio.h>
int main()
{
  printf("Hello World!");
  return 0;
}

The compiling process is like this,
1) compile c code into llvm assembly using clang
2) convert llvm assembly into ARM assembly using llc
3) convert ARM assembly code into binary/ELF using binutils

clang -emit-llvm hello.c -c -o hello.bc

-emit-llvm option instructs clang to generate llvm assembly. The generated llvm assembly, hello.bc can be executed using lli.

lli hello.bc

Let us convert this llvm assembly into ARM assembly,

llc -march=arm hello.bc -o hello.s

Now, we can binutils(as + ld) to assemble and link these generated ARM assembly files.

arm-elf-gcc hello.s -o hello.elf

You can use gdb to simulate the generated elf.

Update:
Here is the single command which compiles, assembles and links the given c code,

clang -march=armv7-a -ccc-host-triple arm-elf -ccc-gcc-name arm-elf-gcc hello.c

writing interrupt routines using gcc for arm

Interrupts make a microprocessor responsive to external impulse. ARM uses Interrupt vector table to determine what to do when a specific interrupt/exception occurs. This interrupt vector table resides at address 0x00000000 and can be remapped to 0xffff0000 in some ARM processors. Each entry in IVT is a branch statement to the corresponding interrupt handler routine. Here is the startup code which sets up the Interrupt Vector table,
startup.S

.global _start
_start:
	b _Reset
	ldr	pc, _undefined_instruction
	ldr	pc, _software_interrupt
	ldr	pc, _prefetch_abort
	ldr	pc, _data_abort
	ldr	pc, _not_used
	ldr	pc, _irq
	ldr	pc, _fiq

_undefined_instruction: .word undefined_instruction
_software_interrupt:	.word software_interrupt
_prefetch_abort:	.word prefetch_abort
_data_abort:		.word data_abort
_not_used:		.word not_used
_irq:			.word irq
_fiq:			.word fiq

_Reset:
	ldr sp, =stack_top
	bl  c_start
	b   .

undefined_instruction:
	b   .

software_interrupt:
	b   do_software_interrupt

prefetch_abort:
	b   .

data_abort:
	b   .

not_used:
	b   .

irq:
	b   do_irq

fiq:
	b   do_fiq

do_software_interrupt, do_irq and do_fiq are interrupt service routines for SWI, IRQ and FIQ respectively. These functions are implemented in c using gcc’s __attribute__ feature. Here is the actual c code containing routines for irq, fiq and software interrupt.

entry.c

void __attribute__((interrupt("IRQ"))) do_irq()
{
	//your irq service code goes here
}

void __attribute__((interrupt("FIQ"))) do_fiq()
{
	//your fiq service code goes here
}

void __attribute__((interrupt("SWI"))) do_software_interrupt()
{
	volatile unsigned int int_num;
	asm("LDR r0, [lr, #-4]");
	asm("BIC r0, #0xFF000000");
	asm("MOV %0, r0":"=r"(int_num):);
	//based on int_num, you can determine which system call is called
}

void c_start() {
	asm volatile ("SVC 0x5");
	while(1){}
}

__attribute__((interrupt(“SWI”))) instructs the compiler that the following function is an interrupt routine. Thus the compiler can generate function entry and exit sequences suitable for use in an interrupt handler. Here is the linker script,
arm.ld

ENTRY(_start)
SECTIONS
{
 . = 0x0;
 .startup . : { startup.out(.text) }
 .text : { *(.text) }
 .data : { *(.data) }
 .rodata : { *(.rodata) }
 .bss : { *(.bss) }
 . = . + 0x1000; /* 4kB of stack memory */
 stack_top = .;
}

make file for this example.

AS=arm-elf-as
ASFLAGS=-g -o
CC=arm-elf-gcc
CCFLAGS=-g -c -nostdlib -o
LD=arm-elf-ld
LDFLAGS=-Tarm.ld -o
GDB=arm-elf-gdb

all: exec

startup.out: startup.S
	${AS} ${ASFLAGS} startup.out startup.S

entry.out: entry.c
	${CC} ${CCFLAGS} entry.out entry.c

exec: entry.out startup.out
	${LD} ${LDFLAGS} exec startup.out entry.out

debug: exec
	qemu-system-arm -M versatilepb -m 128M -nographic -serial /dev/null -kernel exec -s -S

gdb: exec
	${GDB} exec

clean:
	rm *.out exec *.o

You can test the code by executing the command make debug. Line number 21 in entry.c is used to raise a software interrupt. Thus executing info registers in qemu console, should show SWI interrupt number in register 0.

Does anyone know how to generate arbitrary irq or fiq interrupt signals in qemu to test irq and fiq routines? Is it possible?

compile simple ARM bare metal program and simulate it using qemu

Here is a simple ARM assembly program,
startup.S

.global _start
_start:
  B _reset /* Reset */
  B . /* Undefined */
  B . /* SWI */
  B . /* Prefetch Abort */
  B . /* Data Abort */
  B . /* reserved */
  B . /* IRQ */
  B . /* FIQ */

_reset:
  mov r1, #10
  mov r0, #100
  str r1, [r0]
  ldr r2, [r0]
  B .

Assemble it,

arm-elf-as -g startup.S -o startup.out
arm-elf-ld -Ttext=0x0 -o startup.elf startup.out

Since the program is very simple, I haven’t used any linker script here. -Ttext=0x0 option instructs the linker to use 0x0 as the starting address of the instructions. To check the address of each symbols,

arm-elf-nm startup.elf

Now, to simulate it using qemu,

qemu-system-arm -M versatilepb -m 128M -kernel startup.elf

To enter qemu console, press Ctrl+Alt+2. Qemu should display console prompt. To display information about current register values, type,

info registers

To find more about the available commands type help or ?.

LDR – an arm pseudo instruction to load immediate values

We have two dedicated instructions MOV and MVN to move data between registers. But their ability to load immediate values is very limited. They support an 8-bit immediate value, giving only a range of 0-255. These 8 bits can then be rotated right through an even number of positions(ie RORs by 0, 2, 4,..30). Though it gives a much larger range, It misses out large number of constants too. There is no single instruction which will load a 32 bit immediate constant into a register without performing a data load from memory. All ARM instructions are 32-bit long and ARM doesn’t use instruction stream as data. The reason which lead to the creation of LDR instruction. This is a pseudo instruction and is converted into a proper ARM instruction when assembled. The syntax of LDR instruction is,

LDR rd, = [ numeric constant | label ]

This is what the assembler does when it finds LDR instruction,

  • If the constant can be constructed using either a MOV or MVN then this will be the instruction actually generated.
  • Otherwise, the assembler will produce an LDR instruction with a PC-relative address to read the constant from the nearest literal pool.

Thus, these instructions

LDR r1, =5
LDR r2, =-2
LDR r3, =256703
LDR r4, =49152

are converted into

MOV r1, #5
MVN r2, #1
LDR r3, [pc, #8] ; here 8 is the pc relative offset to literal pool where 256703 is stored
MOV r4, #3, 18

why nmos exhibits strong 0 and weak 1

One of the many reasons cmos replaced nmos logic is that, nmos produces strong 0 but weak 1. This is mainly because of the parasitic Threshold voltage(V_{th}). We are going to see how and why this happens. First let us consider a circuit which emphasizes the capability of nmos to produce good zero,

NMOS strong0
Now let us sweep input voltage, V1, from 0 to 5V with 100mV incremental steps. It is obvious that when nmos is on(V1>Vth), the output is grounded or there is very little voltage drop across the nmos. This produces strong 0.

Let us see, how nmos produces a degraded or weak 1 using the following circuit.

Now let us sweep the input voltage, V1, from 0 to 5V with 100mV incremental steps. The output is,

When V1>Vth, We expect the output to be Vdd(=5V). But instead the output is degraded or weak. Even when V1=5V(=Vdd), output is 2.15(Vdd-Vth=5-2.85). I wanted to know why output is Vdd-Vth instead of Vdd. To understand that, I had to plot few more curves.

It happens because this circuit acts as source follower. The resistor(R1) acts as feedback and linearizes Ids(green curve in the upper plot). Hence the source(or output) voltage(red curve in lower plot) linearizes too, and follows the gate voltage(V1, green voltage in lower plot). Since the transistor is on only after V1 exceeds Vth and Vout follows V1 after that, The output is Vth less than Vdd when V1 reaches Vdd. But as we increase V1 further more, Vout reaches Vdd when V1 reaches Vdd+Vth and Vout saturates after that.

Now in the same circuit, let us keep input voltage(V1) high(5V) and sweep V2 from 0 to 8V. As expected, Vout(or source voltage) can never exceed V1-Vth. Again nmos cannot produce strong 1.

To build and and or gates using cmos, We have to use nmos as pull-ups and pmos as pull-downs. Since this produces very inefficient circuit, weak 1 and 0, We don’t use and and or gates as standard gates in cmos.

Now to better understand, let us design a cmos pass buffer from scratch.

Input voltage(V2) is sine generator with frequency 1KHz and amplitude 14.14. Let us do a transient analysis on this circuit.

As expected, the nmos passes negative half efficiently but the positive half has been clamped to 2.13V(V1-Vth=5-2.87). This shows that nmos passes weak 1. We have to add a pmos to this circuit. So that it can take care of passing positive half.

Voila! The full curve is passed efficiently.

2010 in review

The stats helper monkeys at WordPress.com mulled over how this blog did in 2010, and here’s a high level summary of its overall blog health:

Healthy blog!

The Blog-Health-o-Meter™ reads Wow.

Crunchy numbers

Featured image

The average container ship can carry about 4,500 containers. This blog was viewed about 21,000 times in 2010. If each view were a shipping container, your blog would have filled about 5 fully loaded ships.

In 2010, there were 58 new posts, growing the total archive of this blog to 81 posts. There were 58 pictures uploaded, taking up a total of 2mb. That’s about 1 pictures per week.

The busiest day of the year was December 7th with 218 views. The most popular post that day was My Profile.

Where did they come from?

The top referring sites in 2010 were en.wordpress.com, stumbleupon.com, google.co.in, google.com, and gnucap.org.

Some visitors came searching, mostly for terminal find, multiget firefox, gspiceui, find terminal, and shape recognition matlab.

Attractions in 2010

These are the posts and pages that got the most views in 2010.

1

My Profile August 2009
2 comments

2

spice (gschem + gnetlist + gnucap + gwave + gspiceui) @ Linux December 2009
2 comments

3

Find files using Terminal (find and grep) November 2009
1 comment

4

Installing bitbake and openembedded @ Ubuntu February 2010
1 comment

5

Flashing uboot onto nand – mini2440 July 2010
4 comments