Multi task on PIC16Fxx |Naming Symbols | Traffic Generation 1 | Traffic Generation 2 |

Multi Process code on a Pic

It may seem that running multiple processes on such a small chip is a bit of an ordeal. Why do it? Here is a simple way of doing co-operative multi tasking.

A way to simplify a problem is to write a state machine with carefully choosen events and states that best fit the problem. e.g. sports Timer.

Other processes or tasks are used to map the signals, conditions and inputs to the carefully choosen events.

If your code needs to respond to more than one input, the UNIX Client / Server model does not work very well. UNIX champions the remote procedure call. If these block, they need return quickly.

The client sends a request to the server, and the client blocks waiting for a response from the server. The server blocks waiting for connect requests and spawns a new process to handle the connect.

It may seem that running multiple processes or tasks on such a small chip as a PIC16F628 is a bit of an ordeal.

Why split up the problem into separate processes? What is a process?

Process B has to listen to Process A, Process C, the Hardware and respond and report to management.

If you need to repond to multiple sources, you need to poll each source and service any event or significant change of condition.

	Loop:
 	Poll for a change in condition1, and if found send a message to a process.
 	Poll for a change in condition2, and if found send a message to a process.
 
 	For each process.
 		Is there a message to process? if so process it.
 	goto Loop.
 
 

Normally it is bad to loop and poll in a Windows console program as it hogs the processor e.g

   while( true ){
      // wait for character then echo it. 
      // this works well if there is only one input.
      
      putc( getc() );
 
   }  
 
 

On UNIX and other systems you can use select() to sleep the program waiting for an event on I/O or timeout.

	
   Loop:
 
         On Timer Event  
 		Poll for a change in condition1, and if found send a message to a process.
 		Poll for a change in condition2, and if found send a message to a process.
 
 
 	For each process.
 		Is there a message to process? if so process it.
 
         sleep, waiting for any event from a list. Awake on any event being waited for 
 
 
   goto Loop.
 
 
Here are examples:
rt.c I wrote a demonstrator in C for Dos in Borland TurboC back in 1992. It does not matter what it does, only the layout. Turbo C does not have a sleepAwaitingEvent() function.

iic.cpp Here is an attempt to use rs232, and stop spinning by using select() on a socket to sleep a console program complied with Bloodshed CPP.

bc001.asm A Template fot the PIC16F628 ( and PIC16F84 )

timer0.c Dabbles with HI-TECH Software\PICC\9.70\samples\TimerDemo modified for 16F690 and 16F886 demo boards.

The reason that I split my shipping forecast timer ( code: rt001.asm ), up into separate processes was due to the limited depth of the stack on the PIC. If a function wants another bit of code to run, instead of calling a function and using another stack level, there and then, it sets a bit in doTimeSlice, tidies up and returns. The main loop polls the bits in doTimeslice, and calls a routine. This routine resets the bit in doTimeSlice.

My shipping forecast timer splits the routines up so that I have a timer chain, a button read routine, and a routine that polls TMR0. I then have a multi-process application.

My processes are:

	
 	Timer Chain	polls for TMR0 overflowing and generates events.
 	IPtimeSlice	polls for button presses and generates events
 
 	DTsS4event	run 4 times a second
 	DTsSevent		run every second
 	DTsMevent		run every minute
 	DTsHevent		run every hour
 
 	DTSRwrtEvent	scheduled for serial output
 
 	DTSbeepPause	scheduled to pause beep machine in digit 5
 	DTSbeepMachine	scheduled to run beep machine
 

Here is an example of a simple 5 minute timer:

 ;**************************************************************************
 ; 
 ;
 ; PROGRAM: 5 minute timer
 ;
 ; DESCRIPTION: turns on/off LED when buttons pressed. 
 ;			If timer running, times out and turns off LED.
 ;		one button starts for 5 minutes
 ;		another button starts for 15 seconds
 ;
 ;			16F84 clocked by 32.768 kHz
 ;
 ; AUTHOR:  Douglas Rice
 ; Copyright 2010
 ;
 ;
 ;
 	INCLUDE "p16F84.inc"       
 ;
 ;**************************************************************************
 
         LIST P=16F84, R=DEC
 	__idlocs  0x1234
 	; __config  _LP_OSC&_PWRTE_ON & _WDT_OFF
 	__config  _XT_OSC&_PWRTE_ON & _WDT_OFF
 
 ;--------------------------------------------------------------------------
 ; Sec 1. Equates and Constants
 ;--------------------------------------------------------------------------
 ; The General Purpose Registers start at the end of the 
 ; Special Purpose Registers.
 
 ; DoTimeSlice bits
 ; these bits are set to schedule a timer chain event
 ;	DoTimeSlice Bits, to schedule, set bit
 ;	0	run on 1/8th Second
 ;	1	not used
 ;	2	not used
 ;	3	not used
 ;	4	not used
 ;	5	not used
 ;	6	not used
 ;	7	run	BeepMachine
 
 DTsS8event	EQU	0	; run on 1/8th Second
 DTsSevent	EQU	1	; run on Second
 DTsMevent	EQU	2	; run on Minute
 DTsHevent	EQU	3	; run on Hour
 ;
 DTSrunning	EQU	7	; Timer running
   
 ;--------------------------------------------------------------------------
 ; Sec 1.1 Button and LED 
 ;--------------------------------------------------------------------------
 
 ; PORTA bits
 BUTTON_SET_5MIN		EQU	4
 BUTTON_SET_15SEC	EQU	5
 
 ; PORTB bits
 LED			EQU	1
 
 
 ;--------------------------------------------------------------------------
 ; Sec 2.0 Variables
 ;--------------------------------------------------------------------------
 ;
 ; 
   ; cblock 0x20 ; on other chips
   cblock 0x0C ; on 16F84
 ;
 ; Variables start 0x0C
 
 DoTimeSlice		; See bit usage above
 
 CLtmr8sec		; counts 1/8  seconds
 CLnbcdS 		; stores current time
 CLnbcdM 	
 
 IPlast			; Input variables
 IPnew		
 IPbuttonEvent	; stores if button pressed and button event awaiting service
   endc
 
 ;--------------------------------------------------------------------------
 ; Sec 3. Program Code
 ;--------------------------------------------------------------------------
 
 	ORG     0       
     GOTO    Start
 
 	ORG 	4
 	retfie 
 
 ;--------------------------------------------------------------------------
 ; Sec 3.1	Main Program Init Code
 ;--------------------------------------------------------------------------
 
 Start
 	; Disable Interupts
 	MOVLW	H'00'
 	MOVWF	INTCON
 
 	; Set up PortB:7 as output.
 	banksel	TRISB
 	MOVLW	0XFF
 	MOVWF	TRISB
 	MOVLW	0xF0	
 	MOVWF	TRISA
 
 	; Change Prescaler
 	CLRWDT
 	; Configure Tmr 0 to clock off Oscillator running at 32.768kHz
 	; 32768 / 4 in
 	; Set up prescaller for 8192 / 4 / 256 = 8 ticks perseconds.
 	MOVLW	0x0 + 1 	; For Tmr0 0= /2, 1= /4, 2= /8, 3= /16
 	MOVWF	OPTION_REG
 
 	banksel PORTA
 	bsf		PORTA,LED
 	bcf		PORTA,LED
 
 	CLRF	DoTimeSlice	
 	; flush button events
 	call	IPtimeslice
 	call	IPtimeslice
 	; clear any button events
 	CLRF	IPbuttonEvent
 	
 	call 	CLinitTimeout
 	GOTO	MainLoop
 	
 	
 ;--------------------------------------------------------------------------
 ; Sec 3.2	Main Program 
 ;--------------------------------------------------------------------------
 
 MainLoop	
 		; Assuming:
 		; 	Crystal = 32.768 kHz
 		;	Prescaller ticks at Crystal / 4 or 8192 ticks per second.
 		;	Prescaller set to divide by 4 to give 8 timeouts per second 
 		;	Tmr0 times out 8 times per second.
 		;
 		BTFSC	INTCON,T0IF		; test for TMR0 time out
 		CALL	CLtimeslice
 
 		; Any events that exist
 		BTFSC	DoTimeSlice,DTsS8event	; On 1/8 Second Tick
 		CALL	SS8tick					; Polls Switches 8 times a second
 
 		BTFSC	IPbuttonEvent,BUTTON_SET_5MIN	; On BUTTON_SET Press
 		CALL	IPsetPressed
 
 		BTFSC	IPbuttonEvent,BUTTON_SET_15SEC	; On BUTTON_SET2 Press
 		CALL	IPsetPressed15sec
 
 		GOTO 	MainLoop
 
 ;--------------------------------------------------------------------------
 ; Sec 4.	Subroutines, procedures and functions
 ;--------------------------------------------------------------------------
 
 ;--------------------------------------------------------------------------
 ; Sec 4.1	Button Poll Routine
 ;--------------------------------------------------------------------------
 		
 IPtimeslice
 		;	----___
 		;	--_____
 		;
 		; This reads all Port B inputs and looks for Press
 		banksel	IPlast
 		MOVFW	IPnew
 		MOVWF	IPlast
 		MOVFW	PORTB
 		MOVWF	IPnew
 		; IP last contains new setting, IPlast contains previous
 		COMF	IPnew,W
 		ANDWF	IPlast,W
 		; now force IPbuttonEvent bits to high for new pressed button
 		; the service routine should set the bit to clear the event.
 		IORWF	IPbuttonEvent,F
 	
 		RETURN
 
 
 ;--------------------------------------------------------------------------
 ; Sec 4.3	Clock Chain Routine
 ;--------------------------------------------------------------------------
 		
 CLtimeslice
 		; TMR0 timeout
 		BCF	INTCON,T0IF
 		BSF	DoTimeSlice,DTsS8event
 
 		; This ticks 8 per second, 
 		; 
 		DECFSZ	CLtmr8sec,f
 		RETURN
 		; divide by 8
 		MOVLW	8
 		MOVWF	CLtmr8sec
 		
 		btfss	DoTimeSlice,DTSrunning	
 		return
 
 		; Counter	- Seconds
 		; This ticks 1 per second, 
 		; 
 
 CLbcdS
 		BSF	DoTimeSlice,DTsSevent
 		DECFSZ	CLnbcdS,f		
 		RETURN
 		movlw	.60
 		movwf	CLnbcdS
 	
 
 CLbcdM
 		BSF	DoTimeSlice,DTsMevent	
 		DECFSZ	CLnbcdM,f		
 		RETURN
 		movlw	.60
 		movwf	CLnbcdM
 
 		;
 		; 5 minute time out reset running flag and turn off LED
 		;
 		bcf		DoTimeSlice,DTSrunning	
 		bsf		PORTA,LED
 		return
 
 
 
 CLinitTimeout	
 	; using DECFSZ so add 1
 	;
 	movlw	.59 + 1
 	movwf	CLnbcdS
 	
 	movlw	.4 + 1
 	movwf	CLnbcdM
 
 	movlw	8
 	movwf	CLtmr8sec
 
 	return
 
 
 CLinitTimeout15sec
 	MOVLW	.14 + 1
 	MOVWF	CLnbcdS
 	
 	movlw	.0 + 1
 	movwf	CLnbcdM
 
 	MOVLW	8
 	MOVWF	CLtmr8sec
 
 	return
 
 
 ;--------------------------------------------------------------------------
 ; Sec 4.5	Event Handler Routines - Timer
 ;--------------------------------------------------------------------------
 SS8tick		;  do this 8 times a second
 		BCF		DoTimeSlice,DTsS8event
 		; poll key every 8 seconds
 		CALL	IPtimeslice		
 		
 		
 		RETURN
 
 
 ;--------------------------------------------------------------------------
 ; Sec 4.6	Event Handler Routine - Buttons
 ;--------------------------------------------------------------------------
 ;
 
 IPsetPressed	
 		BCF	IPbuttonEvent,BUTTON_SET_5MIN
 	
 		;
 		; test if running
 		;
 		btfss	DoTimeSlice,DTSrunning	
 		goto	IPsetPressed1
 		
 		; running timer so stop it and turn off LED
 		bcf		DoTimeSlice,DTSrunning	
 		bsf		PORTA,LED
 		return
 
 IPsetPressed1	
 		; running not timer so start it and turn on LED
 		bsf		DoTimeSlice,DTSrunning		
 		call	CLinitTimeout
 		bcf		PORTA, LED
 		return
 
 IPsetPressed15sec	
 		BCF	IPbuttonEvent,BUTTON_SET_15SEC
 	
 		;
 		; test if running
 		;
 		btfss	DoTimeSlice,DTSrunning	
 		goto	IPsetPressed15sec_1
 		
 		; running timer so stop it and turn off LED
 		bcf		DoTimeSlice,DTSrunning	
 		bsf		PORTA,LED
 		return
 
 IPsetPressed15sec_1	
 		; running not timer so start it and turn on LED
 		bsf		DoTimeSlice,DTSrunning		
 		call	CLinitTimeout15sec
 		bcf		PORTA, LED
 		return
 
 ;--------------------------------------------------------------------------
 ; Program End
 ;--------------------------------------------------------------------------
 		END
 
 

Multi Process Code on a PIC

Processes run concurrently, at the same time. They send messages between each other.

They react to incoming messages and process them and maybe send a message to another process.

The processes essentially run concurrently, but on a PIC they cannot. However, the processing required on each event should not take too long and each process can be run in turn and then only if they have a message to process.

If a process executes a delay loop, then it cannot the yield processing power of the chip to another process.

My shipping forecast timer beeps out the time when reviewing the time. This uses a process with a state machine as it would take too long to run and would affect the main loop timing.

Testing for events:

The purpose of a process may be to test for a change of condition and on condition change, create an event. It may be run periodically, or as often as possible.

What is important, is that, when a process sends a message, the target process may not run it immediately. Another process may run to process another event. The message is stored until the receiving process can deal with it.

I do this by having a variable called doTimeSlice, The sending process sets a bit, and the receiving process resets the bit when it is run.

This diagram summarises it in logic, event and ack are pulsed high, and the out out is active low :

You must ensure that the events are processed before the next one is generated. The process must not hog the processing. A process may need to use a state machine to achieve this. A process should never loop waiting for a condition. The main loop should run a polling routine that tests for a condition, polling for each condition in turn.

This diagram summarises a latched state machine. To execute each process have a routine called XXtimeSlice. It can be made to run the logic, and each time XXtimeSlice is called is the software equivalent of clocking the latch:

The inputs are sampled, new outputs and states are calculated. ( It may be necessary to double buffer the latch. )

This is the code for the main loop.

 ;--------------------------------------------------------------------------
 ; Sec 3.2	Main Program 
 ;--------------------------------------------------------------------------
 
 MainLoop	
 	; Enable Interupts
 	; Disable Interupts
 MLdisableInt	
 
 		; Poll timers and buttons for changes.
 		CALL	CLtimeslice
 
 		; Any events that exist
 		BTFSC	DoTimeSlice,DTsS4event	; On 1/4 Second Tick
 		CALL	SS4tick
 
 		; Any events that exist
 		BTFSC	DoTimeSlice,DTsSevent	; On Second Tick
 		CALL	SStick
 
 		BTFSC	DoTimeSlice,DTsMevent	; On Minute Tick
 		CALL	SMtick
 
 		BTFSC	DoTimeSlice,DTsHevent	; On Hour Tick
 		CALL	SHtick
 
 		BTFSC	DoTimeSlice,DTSRwrtEvent	; Serial Display
 		CALL	SRwrtTime
 
 
 		BTFSS	IPbuttonEvent,BUTTON_SET	; On BUTTON_SET Press
 		CALL	IPsetPressed
 
 		BTFSS	IPbuttonEvent,BUTTON_NEXT	; On BUTTON_NEXT Press
 		CALL	IPnextPressed
 
 		BTFSS	IPbuttonEvent,BUTTON_ONOFF	; On BUTTON_ONOFF Press
 		CALL	IPonoffPressed
 
 		BTFSS	IPbuttonEvent,BUTTON_OPENED	; On BUTTON_OPENED Press
 		CALL	IPopenedPressed
 
 		GOTO 	MainLoop
 		
 
 
The event code could look like:
 IPnextPressed	
 		; clear event
 		BSF	IPbuttonEvent,BUTTON_NEXT
 
 		; guts of the routine.
 		CALL	LSblink
 
 		; run a state machine
 		MOVLW	0	; event IPnextPressed	
 		CALL 	IPsetJump
 		MOVWF	IPsetState
 
 		;output the state on port A, that has LEDs connected
 		SWAPF	IPsetState,W
 		MOVWF	PORTA
 
 		; return quickly
 		RETURN
 
 

This is how Microchip implement their peripherals. They each set a status bit and the service code is expected to reset it.

Micro chip have a page on their Wiki that illustrates this: Event Driven Modelling example .

To summarise:

	Loop:
 
 
 	Poll for a change in condition1, and if found send a message to a process.
 	Poll for a change in condition2, and if found send a message to a process.
 	Poll for a timer to mature  and if so, send a message to a process.
 
 	For each process.
 		Is there a message to process? if so process it.
 
 
 	; optionally sleep awaiting interupt. 
 	; Use a timer to slow down polling of buttons to allow for switch contact bounce.
 	; This saves energy.
 
 	sleep
 
 
 	goto Loop.
 
 

Micro chip have a page on their Wiki that illustrates this: Event Driven Modelling example .

bc001.asm A Template fot the PIC16F628 ( and PIC16F84 )

Testing for Button presses:

Sometimes you want to do something when a button is pressed or released.

However, often you only want to do something when a button mechanically moved i.e. when the person pushes or releases the button.

The PIC has a weak pull up which on a simple bread board can be used with a switch. Switches bounce, that is, for each press or release, the electrical contact may make / break many times.

This means that the onPress or onRelease event occurs too often, so some debouncing is required.

This diagram summarises it in logic:

This diagram summarises it in logic, using d-type flip flops:

On A Pic I use a simple parallel technique of reading multiple buttons. I call a routine I call IPtimeslice in a timer timeslice routine. This diagram summarises it in timing diagrams:

This diagram summarises it in timing diagrams, with the responding eventHandler cancelling the event:

This is the code I use to detect pressed.

 IPtimeslice
 	;	----___
 	;	--_____
 	;
 	; This reads all Port B inputs and looks for Presses on all 8 bits at once.
 
 
 	MOVFW	IPnew
 	MOVWF	IPlast
 	MOVFW	PORT_BUTTONS ; PORT_BUTTONS is equ on port ( or timer ) of interest.
 	MOVWF	IPnew
 		
 	; IP last contains new setting, IPlast contains previous
 
 	; IPnew ----___
 	; IPlast	--_____
 	;
 
 	; If you want to trigger event on press		
 	COMF	IPnew,W		; ____------
 	ANDWF	IPlast,W		; -----_____ 
 				; ____-_____
 
 	; now force IPbuttonEvent bits to high for new pressed button
 	; the service routine should clear the bit to clear the event.
 	IORWF	IPbuttonEvent,F
 	RETURN
 
 

This is the code I use to detect a press or release.

 IPtimeslice
 	;	----___
 	;	--_____
 	;
 	; This reads all Port B inputs and looks for Presses on all 8 bits at once.
 
 
 	MOVFW	IPnew
 	MOVWF	IPlast
 	MOVFW	PORT_BUTTONS ; PORT_BUTTONS is equ on port ( or timer ) of interest.
 	MOVWF	IPnew
 		
 	; IP last contains new setting, IPlast contains previous
 
 	; IPnew ----___
 	; IPlast	--_____
 	;
 
 	; If you want to trigger event on down and up 
 	MOVFW	IPnew		; ----______
 	XORWF	IPlast,W		; -----_____ 
 				; ____-_____
 
 
 	; now force IPbuttonEvent bits to high for new pressed button
 	; the service routine should clear the bit to clear the event.
 	IORWF	IPbuttonEvent,F
 	RETURN
 
 

State Machines:

A process should react to an event and process it. A process may want to do a different response for each time the event is called, and a state machine is used to track the state.

This diagram summarises a latched state machine. In code if you have a function called XXtimeSlice, It can be made to run the logic as if the latch has been clocked once:

State machines are easy to impliment.

You combine the state and event to identify the routine to run.

Here is a PIC Jump table:

 ;----------------------------------------------------------------
 ; Sec 3.3 	Jump Table for running or time setting mode
 ;---------------------------------------------------------------- 
 
 IPsetJump
 	; Its not a CLICK or HOLD
 	ADDWF	IPsetState,W
 	CLRF	PCLATH ;  Assume that the tabel is in the bottom 256 
 			  byte, and restrict to 4 states.
 	ANDLW	0x03   ; restrict to 4 choices
		
   IF  0 !=  ( high ( IPjmpTable4End ) - high ( IPjmpTable4Start ) 
 )
     Error "Table straddles Page Boundary "
   ENDIF
 IPjmpTable4Start
 	ADDWF	PCL,f
 	; set choice State Table - limited to 8 states
 	GOTO	STtoggle		; SET:OFF,NEXT
 	GOTO 	SoundInit		; SET:OFF,set
 	GOTO	SoundOneDigit		; SET:YELLOW,NEXT
 	GOTO	SoundNextDigit		; SET:YELLOW,set
 IPjmpTable4End
 

Naming of Functions and variables.

It is possible to have the same code that compiles to identical .hex file to be loaded into a PIC chip. However one source code can be impossible to read, and the other easy to understand.

How you name functions variables and lay out the code, is vitally import.

You could write your code and name your variables and functions as you think up the names, but if you have set up processes, give each process or block of functionality a prefix. What I found was that the prefix means that the function or variable belongs to that bit of functionality, and prevents somebody trying to understand your code spending time reading and trying to understanding the wrong bit of code.

I split my shipping forecast timer up into the the following modular blocks:

	CL	- Clock chain functions.
 	ST	- State machine temporary variables
 	LS	- Loud Speaker driver
 	IP	- Input Polling
 	SR 	- Serial Out
 	OP	- Button Open Logger
 	Sound	- Sound process
 
 

So I could group the variables as below:

 ;----------------------------------------------------------------
 ; Sec 2.0 Variables
 ;----------------------------------------------------------------
 ; Variables start 0x0C
 ;
 ; Variables for FlashLED ( FL ) process 
 ;
 
 		;	DoTimeSlice Bits, to schedule, set bit
 		;	0	run 	onQtrSecondTick
 		;	1	run	onSecondTick
 		;	2	run	onMinuteTick
 		;	3	run	onHourTick
 		;	4	not used
 		;	5	run	Serial Port readout
 		;	6	BeepPause
 		;	7	run	BeepMachine
 
 DoTimeSlice	EQU	0x0C 
 
 STstate		EQU	0x0E	
 
 CLtmr4sec	EQU	0x0F	; counts quarter seconds
 
 CLnbcdS 	EQU	0x10	; stores current time
 CLnbcdM 	EQU	0x11
 CLnbcdH 	EQU	0x12
 
 CLdbcdM 	EQU	0x15	; stores the time being input
 CLdbcdH 	EQU	0x16
 
 CLbbcdM 	EQU	0x18	; stores the time being beeped out
 CLbbcdH 	EQU	0x19
 CLTNum		EQU	0x1A	; Index into EEPROM when updating 
 an ON/OFF time
 
 CLTNumChk	EQU	0x1C	; EEPROM index when testing for ON/OFF time
 
 LScnt		EQU	0x1F	; For Loudspeaker beep generation
 LScnt2		EQU	0x20
 
 IPlast		EQU	0x21	; Input variables
 IPnew		EQU	0x22
 IPbuttonEvent	EQU	0x23
 IPsetState	EQU	0x24
 
 SRout 		EQU	0x25	; for RS232 routines
 SRcnt		EQU	0x27
 
 SoundCnt	EQU	0x28	; Counts beeps
 Sound5Cnt	EQU	0x2A	; count for 5th beep
 
 DispState	EQU	0x2B	; Display State
 
 OPcnt		EQU	0x2C	; Count 
 OPopcnt		EQU	0x2D	; Count 
 
 OPbufferStart	EQU	0x30	; 32 bytes of ram that can store Dates

The function names are also grouped by and prefixed with the two letter variable names.

Using Section Headers

I also tried splitting the source code into sections like any good document. I tried putting a comment like:

;----------------------------------------------------------------
; Sec 2.0 Variables
;----------------------------------------------------------------

You could even include a contents comment block, but a grep of "Sec" would produce a contents page.

Apend the structure type - A tautology in context, a reminder out of context

I once worked on a project that had 100,000 symbols. They say that most educated people have a vocabulary of 10,000 words. Being expected to understand the purpose of 100,000 symbols is obviously beyond the educated person. Some structure to the names is required so that you can work out that you can ignore the symbol as irrelevant to what you are working on.

Adding a type coding to the symbol name also tells you how to treat it.

e.g.

Suppose I have a structure to the leakyBucketData.

I could have an array of leakyBucketData, and in my code, I could have a pointer to an instance of leakyBucketData.

If you add postfixs to define the type you end up with symbols:

  
     leakyBucketDataT //- type definition 
     leakyBucketData  //- Instance of leakyBucketDataT
     leakyBucketDataA //- Array of instances of leakyBucketDataT 
     leakyBucketDataP //- Pointer to instance of leakyBucketDataT 
 
 
or
 
     struct leakyBucketDataS { 
 	int   	fill;
 	int 	leak;
     }; 				//- structure definition 
 
     typedef struct leakyBucketDataS leakyBucketDataT; - type definition 
 
     leakyBucketDataT lb; 	    //- instance of leakyBucketData 
     leakyBucketDataT lbArray[100];  //- Array of instances of leakyBucketData 
     leakyBucketDataT *lbPointer;    //- Pointer to instance of leakyBucketData
 
 
or
 
     struct leakyBucketDataS { 
 	int   	fill;
 	int 	leak;
     }; 				//- structure definition 
 
     typedef struct leakyBucketDataS leakyBucketDataT; - type definition 
 
     leakyBucketDataT lb; 	//- instance of leakyBucketData 
     leakyBucketDataT lbA[100];  //- Array of instances of leakyBucketData 
     leakyBucketDataT *lbP; 	//- Pointer to instance of leakyBucketData
 
 
This is much better than just adding 1,2 to the end of the symbol to make it unique:
 
     struct leakyBucketData { 
       int fill;
       int leak;
     };
 
     struct leakyBucketData lb; 		//- instance of leakyBucketData 
     struct leakyBucketData lb1[100];  	//- Array of instances of leakyBucketData 
     struct leakyBucketData *lb2; 	//- Pointer to instance of leakyBucketData
 
 
This is much easier to read and maintain.
  
     leakyBucketDataT             //- type 
     leakyBucketDataT lb;         //- instance
     leakyBucketDataT lbA[100];   //- array  
     leakyBucketDataT *lbP;       //- pointer  
 
 
You could argue that this seems to be tautological as the postfix duplicates the context. lbA is a an array as it has [100] after the definition, lbP is a pointer as it has a * in front of it. However, this means you need to constantly refer to the variable declarations to know the type if you do not postfix the symbol names and use them out of context. This wastes time and adds complexity. What is easier to read and understand
 
     lb  = ...
     lb1 = ... 
     lb2 =  ...
 
 
or
 
     lb        = ... 
     lbArray   = ...
     lbPointer = ...
 
or
 
     lb  = ... 
     lbA = ...
     lbP = ...
 
This is not a novel idea, but does not seem to be a subject taught on computer science courses. I have seen _ and __ prefix symbolds to do the same thing.

Here is the program with pointers dhr.c

Here is some Dephi ( note Delphi puts the type after the variable name )

 
 type
   POFNotifyA = ^TOFNotifyA;
   POFNotifyW = ^TOFNotifyW;
   POFNotify = POFNotifyA;
   TOFNotifyA = packed record
     hdr: TNMHdr;
     lpOFN: POpenFilenameA;
     pszFile: PAnsiChar;
   end;
   TOFNotifyW = packed record
     hdr: TNMHdr;
     lpOFN: POpenFilenameW;
     pszFile: PWideChar;
   end;
   TOFNotify = TOFNotifyA;
 
 

Example C program

 
 /* --- The following code comes from c:\lcc\lib\wizard\textmode.tpl. */
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 
 // ============================
 struct leakyBucketData {
 	int 	fill;
 	int 	leak;
 };
 
 
 /*
 *  old format variable names lb0,lb1,lb2
 * use no postfix. I have run out of names so use lb1,lb2
 */
 
 struct leakyBucketData lb0; 		//- instance of leakyBucketData
 struct leakyBucketData lb1[100]; //- Array of instances of leakyBucketData
 struct leakyBucketData *lb2; 	//- Pointer to instance of leakyBucketData
 
 
 /*
 * Use Type Defs to simplify
 * use a postfix T for Typedef
 */
 typedef struct leakyBucketData leakyBucketDataT; //- type definition
 
 leakyBucketDataT lb; 		//- instance of leakyBucketData
 
 /*
 * new format variable names lb,lbArray,lbPtr
 * use a postfix Array for arrays and Ptr for Pointers
 * This may be a bit verbose
 */
 leakyBucketDataT lbArray[100];  //- Array of instances of leakyBucketData
 leakyBucketDataT *lbPtr; 		//- Pointer to instance of leakyBucketData
 
 /*
 * new format variable names lb,lbA,lbP
 * use a postfix A for Array and P for Pointer
 */
 leakyBucketDataT lbA[100];  //- Array of instances of leakyBucketData
 leakyBucketDataT *lbP; 		//- Pointer to instance of leakyBucketData
 
 // ============================
 void init( void ){
 	int cnt;
 
 	for ( cnt = 0 ; cnt < 100 ; cnt++ ){
 		lb1[ cnt ].fill = cnt;
 		lb1[ cnt ].leak = 50-cnt;
 	}
 
 
 	for ( cnt = 0 ; cnt < 100 ; cnt++ ){
 		lbArray[ cnt ].fill = cnt;
 		lbArray[ cnt ].leak = 50-cnt;
 	}
 
 	for ( cnt = 0 ; cnt < 100 ; cnt++ ){
 		lbA[ cnt ].fill = cnt;
 		lbA[ cnt ].leak = 50-cnt;
 	}
 }
 
 int main(int argc,char *argv[]){
 
 
 	printf("\n=================!");
 	printf("\nHello World!");
 	printf("\n=================!");
 	printf("\n");
 
 	init();
 
 	printf("\n=================!");
 	/*
 	*  old format variable names lb0,lb1,lb2
         * use no postfix. I have run out of names so use lb1,lb2
 	*/
 	printf("\n %d %d ",lb1[3].fill,lb1[50].fill ) ;
 	printf("\n %d %d ",lb1[3].leak,lb1[50].leak );
 
 	lb2 = lb1;
 	printf("\n %d %d ",(*lb2).fill,(*lb2).leak );
 
 	lb2 = &lb1[89];
 	printf("\n %d %d ",lb2->fill,lb2->leak );
 
 	lb2 = &lb1[35];
 	printf("\n %d %d ",lb2->fill,lb2->leak );
 
 	lb2 = lb1;	//
 	lb2 +=10;   // increment point by 10 elements of the array
 	printf("\n %d %d ",lb2->fill,lb2->leak );
 
 	lb2 +=10;   // increment point by 10 elements of the array
 	printf("\n %d %d ",lb2->fill,lb2->leak );
 
 	lb2 +=10;   // increment point by 10 elements of the array
 	printf("\n %d %d ",lb2->fill,lb2->leak );
 
 	lb2 +=10;   // increment point by 10 elements of the array
 	printf("\n %d %d ",lb2->fill,lb2->leak );
 
 
 	printf("\n=================!");
 	/*
 	* new format variable names lb,lbArray,lbPtr
         * use a postfix Array for arrays and Ptr for Pointers
 	* This may be a bit verbose
  	*/
 	printf("\n %d %d ",lbArray[3].fill,lbArray[50].fill ) ;
 	printf("\n %d %d ",lbArray[3].leak,lbArray[50].leak );
 
 	lbPrt = &lbArray[89];
 
 	printf("\n %d %d ",(*lbPrt).fill,(*lbPrt).leak );
 
 	lbPrt = &lbArray[35];
 	printf("\n %d %d ",lbPrt->fill,lbPrt->leak );
 
 	lbPrt = lbA;	//
 	lbPrt +=10;   // increment point by 10 elements of the array
 	printf("\n %d %d ",lbPrt->fill,lbPrt->leak );
 
 	lbPrt +=10;
 	printf("\n %d %d ",lbPrt->fill,lbPrt->leak );
 
 	lbPrt +=10;
 	printf("\n %d %d ",lbPrt->fill,lbPrt->leak );
 
 	lbPrt +=10;
 	printf("\n %d %d ",lbPrt->fill,lbPrt->leak );
 
 
 	printf("\n=================!");
 	/*
 	* new format variable names lb,lbA,lbP
 	* use a postfix A for Array and P for Pointer
 	*/
 	printf("\n %d %d ",lbA[3].fill,lbA[50].fill ) ;
 	printf("\n %d %d ",lbA[3].leak,lbA[50].leak );
 
 	lbP = &lbA[89];
 	printf("\n %d %d ",(*lbP).fill,(*lbP).leak );
 
 	lbP = &lbA[35];
 	printf("\n %d %d ",lbP->fill,lbP->leak );
 
 	lbP = lbA;	//
 	lbP +=10;   // increment point by 10 elements of the array
 	printf("\n %d %d ",lbP->fill,lbP->leak );
 
 	lbP +=10;
 	printf("\n %d %d ",lbP->fill,lbP->leak );
 
 	lbP +=10;
 	printf("\n %d %d ",lbP->fill,lbP->leak );
 
 	lbP +=10;
 	printf("\n %d %d ",lbP->fill,lbP->leak );
 
 	printf("\n=================!");
 
 	return 0;
 }
 
 

Thoughs on Distributed state machines as used in Object Oriented Programming

http://java.sun.com/docs/books/tutorial/java/concepts/ What Is an Object?

An object is a software bundle of related state and behavior. Software objects are often used to model the real-world objects that you find in everyday life. This lesson explains how state and behavior are represented within an object, introduces the concept of data encapsulation, and explains the benefits of designing your software in this manner.

Thoughs on Distributed state machines as used in Object Oriented Programming, and web sites.

Here is combinatory simple logic system.

Here is a simple state machine system, as outputs are fed back to provide history.

Here is a simple state machine system, as outputs are fed back to provide history. A latch is added to clock the state machine.

Note: In software, it is probably impossible to not have a latch. If the state machine is written in a function called ProcessTimeSlice(), then every time the function is called, it is like clocking the state machine latch. In further examples, they will all be latched. There may even be more than one latch, each clocked by their own bit of software.

Here is a state machine where the state information is stored in more than one place. There is an assumption that the state machine is not run continously. Often it is only run when the inputs change or on a regular time tick. Getting the state from a database may require a data base query. Maybe the state is held in a local copy, and either the database is responsible for pushing down updates or the statemachine is responsible for polling for updates.

In the diagram above, any available processor can be given the code for the combinationatory logic and run it. It will need to be given access to the state variables, and to avoid corruption, these will be locked for sole use by the processor. It will need a way of storing the state variables back into any central data store. so:
 
 localCopyOfStateVariables =  StateVariables
 
 
get from array:
 
 localCopyOfStateVariables =  StateVariables[ instanceWanted ]
 
 
get from any where:
 
 localCopyOfStateVariables =  getStateVariables( instanceWanted )
 ...
 putStateVariables(    instanceWanted )
 
 
Here is an outline of running with locks:
 
 lockStateVariables(   instanceWanted )
 localCopyOfStateVariables = getStateVariables( instanceWanted )
 localCopyOfStateVariables = runCombinatoryLogic( localCopyOfStateVariables )
 putStateVariables(    instanceWanted )
 unlockStateVariables( instanceWanted )
 
 

Here is a state machine used to model analogue electronics.

Thoughs on a traffic generators.

A traffic generator can be modelled by a process called once per timer tick. The process sends a traffic message.

You can start the timer with the required interArrival time.
You can start the timer with a fixed time and use probability to choose to send a call.
You can start the timer with a fixed time and use a catch up calculation to accumulate the interArrival Time until you have reached the timer tick. Each accumulation, you send a call.
Here is a traffic generator written in perl. Run these three Perl programs:
udp1.pl First Cuts in PERL - Traffic generator to 127.0.0.1 ports 2346 & 2347
udp2.pl First Cuts in PERL - Traffic sink on port 2346
udp3.pl First Cuts in PERL - Traffic sink on port 2347

udp4.pl First Cuts in PERL - Traffic generator to 127.0.0.1 ports 2346 & 2347 using Catch Up Calculations

A traffic generator can be modelled by a process called once per timer tick. The process sends a traffic message.

You can start the timer with the required interArrival time.
You can start the timer with a fixed time and use probability to choose to send a call.
You can start the timer with a fixed time and use a catch up calculation to accumulate the interArrival Time until you have reached the timer tick. Each accumulation, you send a call.

Thoughs on a traffic generators, and Norton / Thevenin equivalent circuits.

Electronic equivalent circuits come in two forms:
the voltage-source oriented Thévenin equivalent and the current-source oriented Norton equivalent (see figure). Norton @ wikipedia Thevenin @ wikipedia

I have defined terms for call generators:

In a call generator you have three parameters, which can be controlled with the others left to their own devices.

For a constant Interarrival time - calls are started at constant inter arrival times.

For a constant Erlang generator - the circuit occupancy is kept constant. Either by short calls or long calls.

For a constant Circuits generator - the number of circuits carrying calls is kept constant, when call ends another must start.

Where I use the word constant above, you can replace this by modulated. Modulated here means use a distribution like Negative exponential.

So for the "constant" Interarrival times, instead of using interArrivalTime = 10, use something like:

 	# use rand() to generate a random number
         $interArrivalTime = -log( 1 - rand() ) / $CallsPerSecond;
 
Here is a bit of perl code from: udp4.pl: - Traffic generator using Catch Up Calculations.

This modulates the interarrival time and although it uses a constant tick, calls are sent to catch up the number sent.

This code quantises the times to the timer tick, but could be modified to impliment the other two equivalent call generators. Every timer tick you would check if the wanted Erlangs or circuits in use are matched by what is happening.

     # Catch Up calculation method. 
     # A fixed timer is used, and on each tick, 
     # calls are sent to catch up simulated time.
 
     my $nowTime = $timerTickTime;
  
     while( 1 ) {
 
 	if ( $nowTime > 0.0 ) { 
 
            # start timer for a fixed period 
            $count = select( undef, undef, undef, $timerTickTime ) ;
            $nowTime = $nowTime - $timerTickTime;
 
 	} else {        
         # Catch up with calls
         # generate a time interval modulated by Neg Exp distribution
         $interArrivalTime = -log( 1 - rand() ) / $CallsPerSecond;
         $sum         = $sum + $interArrivalTime;
         $running     = time - $StartedAt;
 
 
 	# Now pick a call type
 	$callType    = int( rand()*10 );
 
 	# Now pick a destination
 	$destination = int( rand()*10 );
 
 
         #print "Call Generator: $running,$sum,$callType,$destination,$interArrivalTime \r\n";
  
         #$ip = ;
 
 	# Prepare Message
 	# TS,Protocol,OrigID,DestID,Operation,P1,,,CRLF
 
         $message =  "$sum,0,0,$destination,MAKECAll,,,END\r\n";
 
         if ( rand() < 0.5 ) { 
           $hispaddr = sockaddr_in(2346, inet_aton("127.0.0.1") );
         } else {
           $hispaddr = sockaddr_in(2347, inet_aton("127.0.0.1") );
         }
         send(SOCKET, "CLG:,$message", 0, $hispaddr) ;
 
         print "Call Generator: $running,$sum,  $message";
 
 	$nowTime = $nowTime + $interArrivalTime;
 	}
     }
 
 

Thats enough for now!