Download - Erlang
Erlang
Functional programming for
real-world applications
• functions
• lists
•recursion•“yeah whatever”
– runtime datatype
• imperative– compute by modifying state
• x = x + 2;• overwrite memory “x”
• functional– transform
• f(x) x + 2• produces new value
Functions• are “first class citizens”
• can return functions
• can take functions as arguments
• can be assigned to variables
Necessary precondition, NOT sufficient!* javascript, glish (brrrrr!), python, ...
One function call, multiple bodies• look for name + number of arguments
• notation: “functionname/2”
– not found => EXPLODE!• patternmatch each clause (“body”)
– seems to match?• [arguments satisfy contstraints?]
– do evaluate that clause!• no more clauses?
– EXPLODE!
• sometimes you don’t care– _ is the wildcard; matches anything
QuickTime™ and a decompressor
are needed to see this picture.
Functionbehaviour = mathematical• result depends on arguments alone• same arguments => same result
– user input? file I/O?
• every statement always yields something• maybe wrong but predictable• less side-effects = more pure
– haskell: very pure• I/O special treatment
– Erlang: pragmatically pure• I/O as you’d expect
A function or expression is said to have a
side effect if, in addition to producing a
value, it also modifies some state or has an
observable interaction with calling functions
or the outside world.
int counter = 0;
int plustwo( int x ) {
counter++;
return x+2;
}
Lists
Mylist = [1,2,3].
Yourlist = [ ].
Things = [ “me”, 1, aap ].
Newlist = [ -1 | Things ]. = [ -1, “me”, 1, aap ].
map(Func, List) ListOfResults• transform a list of things into a list of other things
• ingredients:
• 1 List of things
• 1 Function: F(x) y
• produces: [ F(x0), F(x1), ... ], x List
• in fact ... identical to list comprehension
• [ F(X) || X List]
fold(Func, Init, List) Result• a.k.a “reduce”; reduce a list of things to one value
• ingredients:
• 1 List of things
• 1 Initial value
• 1 Function F(x, y) z
• yields: F(Init, F(x0, F(x1, ..))), x List (leftfold)
• or: F(F(F(..., xn-1), xn), Init), x List (rightfold)
F(x,y) x + y, Init = 0, List=[1,2,3]
Init [x0, x1, x2]
F(Init, x0) F0
F0 [x1, x2]
F(F0, x1) F1
F1 [x2]
F(F1, x2) F2
0 [1, 2, 3]
F(0, 1) (0+1)
F0 [2, 3]
F(F0, 2) ((0+1) + 2)
F1 [3]
F(F1, 3) ((0+1)+2)+3)
F(x,y) [x | y], Init = [], List=[1,2,3]
[1, 2, 3] []
F(3, []) [3 | [ ] ]
[1, 2] F0
F(2, F0) [ 2 | [ 3 | [ ] ] ]
[1] F1
F(1, F1) [ 1 | [ 2 | [ 3 | [ ] ] ] ]
[x0, x1, x2] Init
F(x2, Init) F0
[x0, x1] F0
F(x1, F0) F1
[x0] F1
F(x0, F1) F2
A list can be recursively described as:
“a list is an element followed by another list”
[ Element | List ] [ Head | Tail ]
(tail)recursion• typical ingredients:
• 1 List of things
• 1 recursive function
• sometimes secret ingredients:
• helper function having extra argument (accumulator)
• never runs out of stackspace
• example:
• summing the values of all elements in a list
sum( [ ] ) 0;
sum( [Head|Tail] ) Head + sum(Tail).
sum( List ) sum(List, 0).
sum( [ ], Acc ) Acc;
sum( [Head|Tail], Acc ) sum(Tail, Head+Acc).
Variables aren’t!• mathematical sense• single “assignment”
– ‘=‘ matches, not assign
• placeholders, not memorylocations– bind to expression
• exclusively functionscope• 60% (or more) bug reduction!
– most variables are transient, you don’t really need them
• no global state– even more bug reduction– lockfree multithreading
1. x = 42
2. y = x2
3. x = 3
4. y = x2 ?!?
WTF?! No loops?• recursion is the new for/while/do• stackspace?!• tail recursion + side-effect free = stackframe reuse
QuickTime™ and a decompressor
are needed to see this picture.
Datatypes or rather: terms
• 0-9eE+-. (numbers)
• [ <term>, ... ] (list)
• { <term>, ... } (tuple)
• fun( ... ) <expression> end. (functions)
• “ :alphanum: “ (strings)
• lowercase or ‘:alphanum:’ (atom)
• Uppercase (‘variable’)
• << V:n >> (n bits representing V)
Common idioms• somefunction(...)
{ok, <succesreturnvalue> }error
• otherfunction( X, Y )
case somefunction(X+Y) of error ‘oh noes!’; {ok, Value} 42 + Value
end.
• {ok, Result} = somefunction( ... ), otherfunction(Result)
A pragmatic excercise
Shopping list
3 oranges
2.6 l milk
2 bananas
250 cookies
Unit price
uniboard 20000
apples 0.26
bananas 3.14
oranges 0.65
milk 1.15
cookies 0.06
Total amount = ?
... typically you do this:
* a struct/class with members
• type: enum {orange, apple, banana, ...}
• maybe ‘char*’ or std::string
• price/amount: float? double? (template?!)
• for shopping list, unit price list:
• use array []? vector? list?
need initializing
• array/vector - how did it work again?
• struct itemdesc shoplist[] = { (banana, 2.0), ... };
• vector<itemdesc> vd( shoplist, (sizeof(shoplist)/sizeof(shoplist[0]))+1);
• etc.
• iterate over the vector/ for(i=0; i<num_items; i++) ...
• find unit price in pricelist (need to write function)
• multiply by amount, add to sum
• oh yeah, do not forget
• double sum = 0.0;
what you want is this:%% Both the shopping + pricelist are lists
%% of 2-element tuples: an atom + a number
ShoppingList = [ {3, oranges}, {2.5, milk}, ...].
PriceList = [ {uniboard, 20000}, {apples, 0.26}, .... ].For each item in the shoppinglist multiply the amount by the unit
price and sum the results.
Erlang specific• pure enough, yet pragmatic
– sockets/file/user I/O simple– database connectivity (MySQL)
• pattern matching on binary data– protocol analysis– decoding data
• working with binary data per se– arbitrary N-bit values– splitting/joining chunks of binary data– conversion to/from other datatypes
• distributed fault-tolerant systems
Binary pattern matching%% clause 1: binary block which
%% is at least 2*32bits long AND
%% first 32bits read 0xABADDEED
%% clause 2: binary block starting with 0xFFFFFFFF
decode( << 16#abaddeed:32, SeqNo:32/unsigned-little-integer, _/binary >> )
io:format(“Mark5B DiskFrameHeader, SeqNr ~w~n”, [SeqNo]);
decode( << 16#ffffffff:32, _/binary >> )
io:format(“MarkIV tapeframeheader syncword~n”).
%% 14 bit color-space with 5 bits Red, 5 bits Green and 4 bits Blue
%% match-and-extract in one operation
<< Red:5, Green:5, Blue: 4, _/bitstring >> ) = Blob,
%% The symbols “Red” “Green” and “Blue” are usable as numbers here!
io:format(“RGB[~p, ~p, ~p]~n”, [Red, Green, Blue]).
Processes• very lightweight “threads” of execution
– lockfree– may map onto O/S threads– extremely cheap to create
• come for free– no side-effects– no global/shared state
• interact with outside world/each other– only via message passing– e.g. sockets send messages to their owner
• tell apart by Process-ID– so fundamental it’s a built-in type– Erlang knows where a process is running
Distributed Erlang system
• processes run inside Erlang nodes• nodes run on hosts• nodes are identified by their nodename • 1 host may run multiple nodes
– nodename unique ONLY per host: nodename@host
• the set of connected nodes is the system• “registered processes”
– bind a name to a process on a node– send messages to that name
• you don’t have to know where the process is; Erlang does!
• unbelievably trivial to use/build!
In graphical form
HostA HostB
node2
node2
node1
node1
Supervisor process
Worker process
Fault tolerant• processes can be linked• crash? ‘EXIT’ signal propagates along links• ‘EXIT’ signal is just a message• allows one process to monitor one or more other processes
QuickTime™ and a decompressor
are needed to see this picture.
A
C
B
D
QuickTime™ and a decompressor
are needed to see this picture.
UniBoard
QuickTime™ and a decompressor
are needed to see this picture.
4 frontnode
4 backnode
1Gbit/s ethernet
16x 10Gbps
16x 10Gbps
>20 Gbps/link
Memorymapped I/O via UDP/IP
FPGA
1Gbit PHY
VHDLHardwareSoftware Polyphase Filterbank
registers
Delay Module
registers
Nios2CPU
registers
registers
QuickTime™ and a decompressor
are needed to see this picture.
FPGA client library
Registermap:
• type of the register (details follow)
• name of the register (symbolic)
• address of the register in the memory map
How to communicate with the FPGA:
• UDP/IP
• needs IP-address
• emulator (for testing/playing)
one controller one fpga
FPGA client library
fpga:bit( <name>, <bit #>, <address> )
fpga:bitrange( <name>, <startbit>, <nbit>, <address> )
fpga:word( <name>, <address> )
fpga:range( <name>, <# words>, <address> )
Available registerdefinition types for the controller
FPGA client library
fpga:read( <name> )
fpga:write( <name>, <value> )
fpga:or_( <name>, <value> )
fpga:and_( <name>, <value> )
fpga:xor_( <name>, <value> )
Available register commands
FPGA client library
fpga:config_read( <address> )
fpga:config_erase( <address> )
fpga:config_write( <address>, <<256bytes>> )
FLASH-memory commands
FPGA client library
Controller = fpga:fpga(Personality, Protocol, [Options]),
fpga:execute(
Controller,
[ fpga:write(pps_enable, 0),
fpga:write(config_n_sta, 8),
fpga:write(pps_enable, 1) ]
).
Actually executing FPGA commands
Example: firfilter 3 2 1 0 ADDRESS 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+0x00000024 | P N N N | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
P = PPS_ENABLE (1bit) NNN = NUM_TAP (3bit)
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+0x0000abcc | CONTROL/STATUS REGISTER | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
File: firfilter.erl-module(firfilter). %% this module is called firfilter, driving one of those-import(fpga). %% enables us to use functions from the client library-behaviour(fpga.personality). %% this module implements an FPGA personality
%% Return the list of registers defined for this personality.%% Called automatically by the FPGA control frameworkregisters() -> {ok, [
%% a 3 bit field starting at bit 5 in the word at 0x24fpga:bitrange(num_tap, 5, 3, 16#24),%% another bit in that same register/wordfpga:bit(pps_enable, 28, 16#24),%% one word (32bits) at location 0xabccfpga:word(control_status, 16#abcc),
] }.
%% define a high-level command for this personalitystart_filter(FPGA, NumTap) ->
%% disable the PPSfpga:execute(FPGA, fpga:write(pps_enable, 0)),%% read the control status registercase fpga:execute(FPGA, fpga:read(control_status)) of
%% if the control_status register is “1” (zero)%% we must first XOR it with “42” ...1 -> fpga:execute(FPGA, fpga:xor(control_status, 42));%% otherwise do nothing_ -> ok
end,%% now write the number of taps into the register for that and immediately%% after that start the PPS again. So far we only used single commands,%% however, you can easily execute a number of commands in one go:fpga:execute(FPGA, [fpga:write(num_tap, NumTap),
fpga:write(pps_enable, 1)]).
-module(firfilter). %% this module is called firfilter, driving one of those-import(fpga). %% enables us to use functions from the client library-behaviour(fpga.personality). %% this module implements an FPGA personality
%% Return the list of registers defined for this personality.%% Called automatically by the FPGA control frameworkregisters() -> {ok, [
%% a 3 bit field starting at bit 5 in the word at 0x24fpga:bitrange(num_tap, 5, 3, 16#24),%% another bit in that same register/wordfpga:bit(pps_enable, 28, 16#24),%% one word (32bits) at location 0xabccfpga:word(control_status, 16#abcc),
] }.
%% define a high-level command for this personalitystart_filter(FPGA, NumTap) ->
%% disable the PPSfpga:execute(FPGA, fpga:write(pps_enable, 0)),%% read the control status registercase fpga:execute(FPGA, fpga:read(control_status)) of
%% if the control_status register is “1” (zero)%% we must first XOR it with “42” ...1 -> fpga:execute(FPGA, fpga:xor(control_status, 42));%% otherwise do nothing_ -> ok
end,%% now write the number of taps into the register for that and immediately%% after that start the PPS again. So far we only used single commands,%% however, you can easily execute a number of commands in one go:fpga:execute(FPGA, [fpga:write(num_tap, NumTap),
fpga:write(pps_enable, 1)]).
int main() { clock_t nof_ticks_report; TUInt32 aTest[4] = {0, 0, 0, 0}; // prepare for entering mainloop nof_ticks_report = clock(); while( 1 ) { clock_t now = clock(); // Task: Ethernet traffic if( (ptPacket=NET_Receive())!=NULL ) handlePacket(ptPacket);
// Report if( (now-nof_ticks_report)>=2000 ) { printf("aTest[0x%08lx] %08lx %08lx\n", &aTest[0], aTest[0], aTest[1]); nof_ticks_report = now; } }}
1. Ctrl = uniboard:start(“10.99.0.0”).
2. uniboard:execute(Ctrl, fn0, fpga:read(pps_enable)).
1
3. uniboard:execute(Ctrl, [1,6], fpga:read(pps_enable)).
[{fn1, 0} , {bn2, 0}]
1. uniboard:read_sensors(Ctrl, all, fpga_temp).
[{fn0, 28}, {fn1, 30}, {fn2, timeout}, .... {bn3, 32}]
2. uniboard:execute(Ctrl, fn, fpga:read(pps_enable)).
[{fn0, 1}, {fn1, 0}, {fn2, timeout}, {fn3, 1}]
3. uniboard:execute(Ctrl, [fn1, bn2], fpga:write(pps_enable, 1)).
[{fn1, ok}, {bn2, ok}]
4. uniboard:execute(Ctrl, [{fn,1}, {bn,1}], fpga:read(pps_enable)).
[{fn1, 1}, {bn1, 0}]
File: uniboard/i2c.erlread_sensors(FPGA, Sensors) -> %% From the sensorslist, build the contents of the I2C %% protocol region (the commands to read them) I2CProto = mk_i2cproto(Sensors), %% We must write that to the i2c-command region of the memory-mapped %% i2c master on the fpga. then we must write the activate bit %% in the command register. Actually, we must make that bit go %% through an 0 -> 1 transition, which we will force by successively %% writing a 0 and then a 1. Force all actions to return "ok". %% (or rather, explode if they don't) I2CResult = case fpga:execute(FPGA, [fpga:write(i2c_proto, I2CProto), fpga:write(i2c_activate, 0), fpga:write(i2c_activate, 1)]) of [ok, ok, ok] -> %% Having done that, now, in theory, %% we should wait for the i2c interrupt %% to appear. Let's wait 1/5th of a second ... timer:sleep(200), %% Now read the full result region and extract the %% values the i2c cruft has given us fpga:execute(FPGA, fpga:read_binary(i2c_result)); [timeout,timeout,timeout] -> %% Bollox. A timeout. Let's pass this on timeout %% No other cases - the code should explode in this case %% since something happened that we don't expect. %% The FPGA commands should either succeed or timeout. end, decode_i2c_result(I2CResult, Sensors).
decode_i2c_result([Word|MoreWords], [_Sensor|MoreSensors], Acc) -> %% The i2c result is returned as 1 byte of value and %% one byte status. %% Status==0 => read succeeded, %% Status==1 => read failed {Value, Remain} = case split_binary(Word, 2) of {<<_:8, 1:8>>, R1} -> {fail, R1}; {<<V:8/unsigned-little, 0:8>>, R2} -> {V, R2} end, decode_i2c_result([Remain|MoreWords], MoreSensors, [Value|Acc]).
UniBoard commandpacketPacketformat
Generic instruction format
Wait-for-1PPS instruction prefix
Write config data [program FLASH image]
Available commandsRead N 32bit words starting from START ADDRESS
(Over)Write N 32bit words from the packet to START ADDRESS
Read/Modify/Write N 32bit words from START ADDRESS + packet to START ADDRESS
32 bits 32 bits 32 bits 0x01 N START ADDRESS
32 bits N * 32 bits | 0 bits
(!)START ADDRESS DATA
32 bits 32 bits 32 bits N*32 bits 0x02 N START ADDRESS N 32 bit words of DATA
32 bits
(!)START ADDRESS
32 bits 32 bits 32 bits N*32 bits OPCODE N START ADDRESS N 32bit words of MASK
OPCODE 0x03 0x04 0x05
BITWISE OPERATION AND OR XOR
32 bits
(!)START ADDRESS
empty