Saturday, October 30, 2021

0000 0000 0111 1001

Bigger random numbers

A little while ago I wrote about how to generate LFSR "random" 8-bit numbers using AVR assembly (specifically the ATTiny13).

The Padauk PFS154 has 3 11-bit PWM channels and so I've been doing a few videos on 16-bit arithmetic on an 8-bit micro.

  1. 16-bit addition and subtraction
  2. 16-bit multiplication

The next part of this crazy project is to scale up the 8-bit random numbers to 16-bit. An LFSR basically works by a LSL/EOR combo, and the 16-bit version is no different.

; AVR ASM program to make "random" 16-bit numbers
; using LFSR with a seed, and two spanners!
; A N Peck Sat 21 Aug 2021 11:02:58 AEST

.nolist
.include "tn13Adef.inc" ; Define device ATtiny13A
.list

.equ span1 = 23         ; count before spanner1 thrown
.equ spanum1 = 7        ; size of spanner1
.def spanner1 = r18     ; spanner1 counting register

.equ span2 = 37         ; count before spanner2 thrown
.equ spanum2 = 3        ; size of spanner2
.def spanner2 = r19     ; spanner2 counting register
.equ seed = 2901        ; starting seed - happy birthday

.equ poly1 = 13         ; change these
.equ poly2 = 24         ; polynomials if need

.dseg
.org SRAM_START
.cseg
.org 000000

rjmp Main ; Reset vector

Main:
; initialise stack
ldi r16, Low(RAMEND)
out SPL, r16

; seed for LFSR loaded
ldi r16, low(seed)
mov r7, r16
ldi r16, high(seed)
mov r6, r16

; polynomial loaded
ldi r24, poly1
ldi r25, poly2

; spanners loaded
ldi r18, span1
ldi r19, span2

Loop:

; generate random number
rcall rand_16

checkspanner1:

subi spanner1, 1          ; decrement spanner1 count
brne nospanner            ; branch if not at zero
 
mov r16, r7               ; throw spanner1 (subtraction)
subi r16, spanum1

breq nozero1              ; result must be nonzero
mov r7, r16

nozero1:

ldi spanner1, span1       ; reset spanner1 count

checkspanner2:

subi spanner2, 1          ; decrement spanner2 count
brne nospanner            ; branch if not at zero

mov r16, r6               ; throw spanner2 (addition)
subi r16, -spanum2

breq nozero2              ; result must be nonzero
mov r6, r16

nozero2:

ldi spanner2, span2       ; reset spanner2 count

nospanner:

; save registers to file with modified nop
nop

rjmp loop

rand_16:
lsl r6        ; shift first
rol r7        ; roll
brcc noxor    ; check flag

eor r6, r24   ; XOR if needed
eor r7, r25

noxor:

ret

To test the randomness of the generated numbers I used a special version of Gerd's AVR Simulator (thanks Gerd!) which allowed me to capture the register values in a csv file. I then used CONCATENATE and HEX2DEC in a spreadsheet to make a long list of decimal values in the range 0 to 65535 (around 6000 values).

Finally I imported this data as raw input into Audacity and then listened to the noise resulting.

This allowed me to tinker with the code until a "white noise" signal resulted - I especially worked two "spanners" into the code whereby after a certain pre-determined number of numbers the random number was "jumped" up or down by a specific amount. Crude, but effective.

Another option would be to swap nibbles at "random" points (I may still code this).


The next part of this project is to see how easy it is to incorporate assembly code such as this (based on the ATTiny13 AVR instruction set) to the PFS154. Watch this space.







No comments:

Post a Comment