The Ivy Bridge

Ivysim

Home

Perl

Tools

Picoblaze

Spartan3E
Starter Kit









PicoBlaze Number Conversion

Decimal to Binary

Microcontrollers often have to convert between number formats. We are used to using decimal numbers, number base ten. Machines work optimally in binary, number base two. This example PSM program for the PicoBlaze demonstrates one technique for converting from decimal to binary.

Decimal numbers are stored as strings of ASCII bytes in Scratch Pad memory. ASCII codes 48-57 representing the numbers 0-9. From right to left, low address to high address, units, tens, hundreds, etc.

Binary numbers are stored in bytes from right to left, low address to high address, 1s, 256s, 65536s, etc. In this example both decimal and binary numbers are Little Endian.

Algorithm

The main routine, convert_to_binary, begins by zeroing the binary memory. It then works through the decimal digits in memory from left to right, character by character, most significant digit first, using a pointer. For each decimal digit the binary number is first multiplied by ten then the decimal digit added.

There is no multiply instruction. Furthermore the add instruction works only on one byte. However, the add instruction does generate a carry bit which is used to ripple additions left to more significant bytes. Multiplying by ten is done byte by byte from left to right, most significant byte first. Each binary byte is copied into a register then added nine times, the equivalent of multiplying by ten. Carry bits are detected after each addition and rippled left as necessary.

The Program

This program can be studied without modification in a simulator, ideally with the PicoBlaze Debugger. Any VHDL design with a PicoBlaze KCPSM3 in it will do because it doesn't actually read or write anything through the ports and ignores interrupts. The <bp> tags inside comments are ignored by the assembler, however, PicoBlaze Debugger inserts break points. Interesting stages of the demonstration are sign posted by <bp> tags. A series of Run to break point debugger commands illustrate its operation.

PicoBlaze Debugger displays the Scratch Pad memory graphically so that you can easily view its state. If you are using the Evaluation Licence then type pb_state in ModelSim's console window. You should see something like this:

#   /spartan3e_tb/uut/inst_s3e/picoblaze1/
#   PC                            02B
#   sF..s8    00 00 00 00 00 00 00 0C
#   s7..s0    00 00 FF 3E 0C 6D 01 00
#   Zero                            1
#   Carry                           0
#   m3F..m38  00 00 00 00 00 00 00 00
#   m37..m30  00 00 00 00 00 00 00 00
#   m2F..m28  00 00 00 00 00 00 00 00
#   m27..m20  00 00 00 00 00 00 00 00
#   m1F..m18  00 00 00 00 00 00 00 00
#   m17..m10  00 00 00 00 00 00 00 00
#   m0F..m08  FF FF 16 6D 00 00 34 32
#   m07..m00  39 34 39 30 37 35 30 31
# 
#   Graphical read/write for Scratch Pad memory is
#   supported by a Full PicoBlaze Debugger licence.

Memory addresses m09..m00 store ASCII characters for the decimal number 4294907501. Memory addresses m0F..m0C store the bytes for the converted binary number, shown in hexadecimal, 0xFFFF166D.

Program File

The PSM program is shown in this box. For convenience it can be downloaded dec2bin.psm too.

  ; This PicoBlaze program demonstrates how to take a decimal number stored as an ASCII
  ; string in 10 bytes of memory and convert it into a 32 bit binary number stored in 4
  ; bytes of memory.
  ;
  ; This algorithm uses multiple 8-bit addition with carry.
  ;
  ; The test routine tries these two examples:
  ;
  ;   Decimal Input    Hexadecimal Result
  ;   4294907501       FFFF166D
  ;   4027448014       FOODFACE
  ;
  ; This program calls test_routine to demonstrate the use of these procedures:
  ;
  ;   zero_decimal_memory
  ;   zero_binary_memory
  ;   convert_to_binary
  ;   multiply_binary_by_ten
  ;   add_to_binary
  ;
  ; Your program should reuse these procedures, associated named register and
  ; constant declarations.
  ;
  ; The number of decimal digits can be changed by modifying decimal_address_left and
  ; decimal_address_right. The number of binary bytes can be changed by modifying
  ; binary_address_left and binary_address_right.
  ;
  ; This program was developed using the IvySim PicoBlaze Debugger:
  ;
  ;   www.ivysim.com/picoblaze/debug/
  ;
  ; Copyright (c) 2010 Ivybridge Simulation
  ; 3, April 2010
  ; www.ivysim.com
  ;

    constant character_0, 30
    constant character_1, 31
    constant character_2, 32
    constant character_3, 33
    constant character_4, 34
    constant character_5, 35
    constant character_6, 36
    constant character_7, 37
    constant character_8, 38
    constant character_9, 39
    constant character_A, 41
    constant character_B, 42
    constant character_C, 43
    constant character_D, 44
    constant character_E, 45
    constant character_F, 46

    constant binary_address_right, 0C
    constant binary_address_left,  0F  ; Four bytes, 32 bits, 8 hex digits

    constant decimal_address_right, 00
    constant decimal_address_left,  09  ; Ten bytes, ten characters, 10 decimal digits

    namereg s0, reg0
    namereg s1, reg1
    namereg s2, reg2
    namereg s3, reg3
    namereg s4, reg4
    namereg s5, reg5
    namereg s6, memory_pointer_dec
    namereg s8, memory_pointer_bin

  initialise:
    disable interrupt

  main:
    call    test_routine  ;<bp>
  jump      main



  zero_decimal_memory:
    load    memory_pointer_dec, decimal_address_left
    load    reg1, 00
  zero_decimal_memory_loop:
    store   reg1, (memory_pointer_dec)
    compare memory_pointer_dec, decimal_address_right
  return    z ;Return when all dec digits are zero
    sub     memory_pointer_dec, 01
  jump      zero_decimal_memory_loop

  zero_binary_memory:
    load    memory_pointer_bin, binary_address_left
    load    reg1, 00
  zero_binary_memory_loop:
    store   reg1, (memory_pointer_bin)
    compare memory_pointer_bin, binary_address_right
  return    z
    sub     memory_pointer_bin, 01
  jump      zero_binary_memory_loop

  ; Convert from decimal to binary
  ;
  ; Strategy is:
  ;   Zero binary memory
  ;   Loop ten times, once for each decimal digit, left to right
  ;     Multiply binary by ten
  ;       From left to right bytes, add 9 times
  ;         Ripple carry to the left
  ;     Add decimal digit to the right byte
  ;       Convert from ASCII to binary (0-9)
  ;       Ripple carry bits to the left
  convert_to_binary:
    call    zero_binary_memory
    load    memory_pointer_dec, decimal_address_left          ; <bp>
  convert_to_binary_loop:
    call    multiply_binary_by_ten
    fetch   reg1, (memory_pointer_dec)
    sub     reg1, character_0         ; No checking, must be 0-9
    load    memory_pointer_bin, binary_address_right
    call    add_to_binary
    compare memory_pointer_dec, decimal_address_right
  return    z
    sub     memory_pointer_dec, 01
  jump      convert_to_binary_loop

  ; Multiply the binary number by 10
  ; reg3 Binary byte address, descending left to right.
  ;      Necessary because memory_pointer_bin increments while carrying.
  ; reg4 Binary byte to add 9x.
  ; reg5 Multiple addition counter: 8,7,6,5,4,3,2,1,0
  multiply_binary_by_ten:
    load    reg3, binary_address_left
  multiply_binary_by_ten_loop:
    load    memory_pointer_bin, reg3
    fetch   reg4, (memory_pointer_bin)
    load    reg5, 08
  multiply_binary_by_ten_loop2:
    load    reg1, reg4
    load    memory_pointer_bin, reg3
    call    add_to_binary
    sub     reg5, 01
  jump      nc, multiply_binary_by_ten_loop2  ; Loop 9 times
    compare reg3, binary_address_right
  return    z ; when all binary bytes have been added 9x
    sub     reg3, 01
  jump      multiply_binary_by_ten_loop

  ; Add reg1 to the binary byte at address memory_pointer_bin.
  add_to_binary:
    fetch   reg2, (memory_pointer_bin)
    add     reg2, reg1
    store   reg2, (memory_pointer_bin)
  return    nc ; when nothing to ripple carry
    compare memory_pointer_bin, binary_address_left
  return    z  ; when no more bytes to carry to
    load    reg1, 01
    add     memory_pointer_bin, 01
    call    add_to_binary ; Carry 1 into binary byte to the left
  return

  test_routine:
    call    zero_decimal_memory  ;<bp>
    ; Load the decimal number 4294907501 in the decimal scratch pad memory area
    load    memory_pointer_dec, decimal_address_left  ;<bp>
    load    reg1, character_4
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_2
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_9
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_4
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_9
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_0
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_7
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_5
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_0
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_1
    store   reg1, (memory_pointer_dec) 
    call    convert_to_binary  ;<bp>
    ; Should see 0xFFFF166D in binary memory address
    call    zero_decimal_memory  ;<bp>
    ; Load the decimal number 4027448014 in the decimal scratch pad memory area
    load    memory_pointer_dec, decimal_address_left  ;<bp>
    load    reg1, character_4
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_0
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_2
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_7
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_4
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_4
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_8
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_0
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_1
    store   reg1, (memory_pointer_dec) 
    sub     memory_pointer_dec, 01
    load    reg1, character_4
    store   reg1, (memory_pointer_dec) 
    call    convert_to_binary  ;<bp>
    ; Should see 0xFOODFACE in binary memory address
  return

Perl Prototype

Before writing a line of PicoBlaze assembly code the algorithm was first prototyped in Perl. It took about 10 minutes to create this short Perl script, less time than it took to document and publish on this webpage.

  #!/usr/bin/perl

  # This short Perl script demonstrates how to convert
  # a decimal number stored as an ASCII string in 10
  # bytes of memory and convert it into a 32 bit binary
  # number stored in 4 bytes of memory.
  #
  # This algorithm is going to be implemented on a
  # simple 8-bit PicoBlaze processor using 8 bit
  # addition and carry detection.
  #
  # The test example takes 4294907501 in decimal and
  # should produce FFFF166D in hexadecimal.
  #
  # 3, April 2010
  # www.ivysim.com
  #

  @dec = split( //, '4294907501' );
  @bin = ( 0 ) x 4;

  foreach $digit ( @dec ) {
    # Multiply the binary number by ten.
    # From right to left, copy the byte and add it nine times.
    foreach $index ( 0..3 ) {
      $byte = $bin[$index];
      foreach( 1..9 ) {
        add( $index, $byte );
      }
    }
    print_bin_in_hex( 'x10:   ' );
    # Take the decimal digit and add that to the least significant binary byte.
    add( 3, $digit );
    print_bin_in_hex( "Add $digit: " );
  }

  sub add {
    # Add the byte at the given index,
    # modulus 256, carry remainder to the left.
    my( $index, $byte ) = @_;
    foreach my $byte_index ( reverse 0..$index ) {
      if( $byte ) {
        $bin[$byte_index] += $byte;
        if( $bin[$byte_index] & 256 ) {
          # Carry one to next byte
          $byte = 1;
          $bin[$byte_index] %= 256;
        } else {
          # No carry so return
          return;
        }
      }
    }
  }

  sub print_bin_in_hex {
    my $label = shift;
    print $label;
    foreach my $byte_index ( 0..3 ) {
      printf "%02X", $bin[$byte_index];
    }
    print "\n";
  }

Here is the output showing that 4294907501 in decimal is equivalent to FFFF166D in hexadecimal.

  c:\> perl dec2bin.pl
  x10:   00000000
  Add 4: 00000004
  x10:   00000028
  Add 2: 0000002A
  x10:   000001A4
  Add 9: 000001AD
  x10:   000010C2
  Add 4: 000010C6
  x10:   0000A7BC
  Add 9: 0000A7C5
  x10:   00068DB2
  Add 0: 00068DB2
  x10:   004188F4
  Add 7: 004188FB
  x10:   028F59CE
  Add 5: 028F59D3
  x10:   1999823E
  Add 0: 1999823E
  x10:   FFFF166C
  Add 1: FFFF166D

It is quicker and easier developing algorithms using the speed and convenience of a high level interpreted language on a workstation. Keeping in mind the limitations of the target instruction set. Writing PicoBlaze code becomes a much smaller step.

Xilinx and PicoBlaze are trademarks of Xilinx Inc.

Copyright © 2006-2018 Ivybridge Simulation Email: info6@ivysim.com Phone: 07704 874512