Transcript 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?
QuickTime™ and a
decompressor
are needed to see this picture.
• [arguments satisfy contstraints?]
– do evaluate that clause!
• no more clauses?
– EXPLODE!
• sometimes you don’t care
– _ is the wildcard; matches anything
Functionbehaviour = mathematical
• result depends on arguments alone
• same arguments => same result
A
is said to have a
int function
counter
=or0;expression
– user input?
file I/O?
• every statement always yields something
side
effect
if,
in
addition
to
producing
a
value,
int• plustwo(
int
x
)
{
maybe wrong but predictable
less
side-effects
more pure
it • also
modifies =some
state or has an
counter++;
– haskell: very pure
• I/O special treatment
observable
interaction with calling functions
–return
Erlang: x+2;
pragmatically pure
• I/O as you’d expect
or
} the outside world.
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
0
[x0,2,
x1,3]x2]
[1,
F(Init,
F0
F(0,
1)x
(0+1)
0) 
F0
[x1,3]
x 2]
[2,
x 1) 
((0+1)
F1
F(F0, 2)
+ 2)
F1
[x2]
[3]
x2)
((0+1)+2)+3)
F2
F(F1, 3)
F(x,y)  [x | y], Init = [], List=[1,2,3]
[x
x1,3]x2[]]
[1,0,2,
Init
F(3,2, [])
F(x
Init)
[3F|0[ ] ]
[x
x1] FF0 0
[1,0, 2]
F(2,1, FF00)) 
F(x
 [F21 | [ 3 | [ ] ] ]
[x
[1]0] FF11
F(1,0, FF11)) 
F(x
 [F12 | [ 2 | [ 3 | [ ] ] ] ]
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
sum( List
)  ingredients:
sum(List, 0).
• sometimes
secret
sum( function
[ ] )  0;having extra argument (accumulator)
• helper
sum(runs
[[Head|Tail]
], Acc
)  Head
 +
Acc;
sum(Tail).
• never
out)of stackspace
sum( [Head|Tail], Acc )  sum(Tail, Head+Acc).
• example:
• summing the values of all elements in a list
Variables aren’t!
• mathematical sense
• single “assignment”
– ‘=‘ matches, not assign
•1.placeholders,
x = 42 not memorylocations
– bind to expression
• exclusively
2. y = x2 functionscope
• 60% (or more) bug reduction!
– most variables are transient, you don’t really need them
3. x = 3
• no global state
– even more bug reduction
2
– lockfree multithreading
4. y = x ?!?
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
Unit price
3 oranges
uniboard 20000
2.6 l milk
apples
2 bananas
bananas 3.14
250 cookies
oranges 0.65
0.26
milk
1.15
cookies
0.06
Total amount = ?
... typically you do this:
need initializing
* a struct/class
• array/vector
with
- how
members
did it work again?
• struct
type:itemdesc
enumshoplist[]
{orange,
= { (banana,
apple,
2.0),
banana,
... };
...}
• vector<itemdesc>
maybe ‘char*’
vd(or
shoplist,
std::string
(sizeof(shoplist)/sizeof(shoplist[0]))+1);
• etc.
price/amount: float? double? (template?!)
• iterate
for shopping
over the
list,vector/
unit price
for(i=0;
list: i<num_items; i++) ...
• find
use unit
arrayprice
[]? vector?
in pricelist
list?(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}, ...].
For
each =item
in the{apples,
shoppinglist
PriceList
[ {uniboard, 20000},
0.26}, .... ].
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
node1
node2
node2
Supervisor process
Worker process
node1
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
A
B
QuickTime™ and a
decompressor
are needed to see this picture.
D
QuickTime™ and a
decompressor
are needed to see this picture.
C
UniBoard
4 frontnode
4 backnode
QuickTime™ and a
decompressor
are needed to see this picture.
>20 Gbps/link
16x 10Gbps
16x 10Gbps
1Gbit/s ethernet
Memorymapped I/O via UDP/IP
VHDL
Hardware
Software
Nios2
CPU
registers
Polyphase Filterbank
registers
registers
Delay Module
registers
1Gbit PHY
FPGA
QuickTime™ and a
decompressor
are needed to see this picture.
FPGA client library
one controller  one fpga
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)
FPGA client library
Available registerdefinition types for the controller
fpga:bit( <name>, <bit #>, <address> )
fpga:bitrange( <name>, <startbit>, <nbit>, <address> )
fpga:word( <name>, <address> )
fpga:range( <name>, <# words>, <address> )
FPGA client library
Available register commands
fpga:read( <name> )
fpga:write( <name>, <value> )
fpga:or_( <name>, <value> )
fpga:and_( <name>, <value> )
fpga:xor_( <name>, <value> )
FPGA client library
FLASH-memory commands
fpga:config_read( <address> )
fpga:config_erase( <address> )
fpga:config_write( <address>, <<256bytes>> )
FPGA client library
Actually executing FPGA commands
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) ]
).
Example: firfilter
ADDRESS
3
2
1
0
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).
-module(firfilter).%%
%%this
thismodule
moduleis
iscalled
calledfirfilter,
firfilter,driving
drivingone
oneof
ofthose
those
-import(fpga).
-import(fpga).%%
%%enables
enablesus
usto
touse
usefunctions
functionsfrom
fromthe
theclient
clientlibrary
library
-behaviour(fpga.personality).
-behaviour(fpga.personality).%%
%%this
thismodule
moduleimplements
implementsan
anFPGA
FPGApersonality
personality
%%
%%Return
Returnthe
thelist
listof
ofregisters
registersdefined
definedfor
forthis
thispersonality.
personality.
%%
%%Called
Calledautomatically
automaticallyby
bythe
theFPGA
FPGAcontrol
controlframework
framework
registers()
registers()->
->
{ok,
{ok,[[
%%
%%aa33bit
bitfield
fieldstarting
startingat
atbit
bit55in
inthe
theword
wordat
at0x24
0x24
fpga:bitrange(num_tap,
fpga:bitrange(num_tap,5,
5,3,
3,16#24),
16#24),
%%
%%another
anotherbit
bitin
inthat
thatsame
sameregister/word
register/word
fpga:bit(pps_enable,
fpga:bit(pps_enable,28,
28,16#24),
16#24),
%%
%%one
oneword
word(32bits)
(32bits)at
atlocation
location0xabcc
0xabcc
fpga:word(control_status,
fpga:word(control_status,16#abcc),
16#abcc),
]]}.
}.
%%
%%define
defineaahigh-level
high-levelcommand
commandfor
forthis
thispersonality
personality
start_filter(FPGA,
start_filter(FPGA,NumTap)
NumTap)->
->
%%
%%disable
disablethe
thePPS
PPS
fpga:execute(FPGA,
fpga:execute(FPGA,fpga:write(pps_enable,
fpga:write(pps_enable,0)),
0)),
%%
%%read
readthe
thecontrol
controlstatus
statusregister
register
case
casefpga:execute(FPGA,
fpga:execute(FPGA,fpga:read(control_status))
fpga:read(control_status))of
of
%%
%%if
ifthe
thecontrol_status
control_statusregister
registeris
is“1”
“1”(zero)
(zero)
%%
%%we
wemust
mustfirst
firstXOR
XORit
itwith
with“42”
“42”...
...
11->
->fpga:execute(FPGA,
fpga:execute(FPGA,fpga:xor(control_status,
fpga:xor(control_status,42));
42));
%%
%%otherwise
otherwisedo
donothing
nothing
__->
->ok
ok
end,
end,
%%
%%now
nowwrite
writethe
thenumber
numberof
oftaps
tapsinto
intothe
theregister
registerfor
forthat
thatand
andimmediately
immediately
%%
%%after
afterthat
thatstart
startthe
thePPS
PPSagain.
again.So
Sofar
farwe
weonly
onlyused
usedsingle
singlecommands,
commands,
%%
%%however,
however,you
youcan
caneasily
easilyexecute
executeaanumber
numberof
ofcommands
commandsin
inone
onego:
go:
fpga:execute(FPGA,
fpga:execute(FPGA,[fpga:write(num_tap,
[fpga:write(num_tap,NumTap),
NumTap),
fpga:write(pps_enable,
fpga:write(pps_enable,1)]).
1)]).
int main() {
clock_t
TUInt32
nof_ticks_report;
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.erl
read_sensors(FPGA,
Sensors) ->
decode_i2c_result([Word|MoreWords],
[_Sensor|MoreSensors], Acc) ->
%% From
the result
sensorslist,
build as
the
of the
I2C
The i2c
is returned
1 contents
byte of value
and
%% protocol
one byte region
status.(the commands to read them)
I2CProto
= mk_i2cproto(Sensors),
%%
Status==0
=> read succeeded,
%% We Status==1
must write
the i2c-command region of the memory-mapped
=>that
readto
failed
%%
i2c master
on =the
fpga.
then we must write
the activate bit
{Value,
Remain}
case
split_binary(Word,
2) of
%% in the command register.
we must make that
bit{fail,
go
{<<_:8, Actually,
1:8>>,
R1} ->
R1};
%% through an 0 -> 1 {<<V:8/unsigned-little,
transition, which we will
forceR2}
by ->
successively
0:8>>,
{V,
R2}
%%
writing a 0 and then a 1. Force all actions to return "ok".
end,
%%
(or rather, explode if they don't) MoreSensors, [Value|Acc]).
decode_i2c_result([Remain|MoreWords],
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).
UniBoard commandpacket
Packetformat
Generic instruction format
Wait-for-1PPS instruction prefix
Write config data [program FLASH image]
Available commands
Read N 32bit words starting from START ADDRESS
32 bits
32 bits
32 bits
32 bits
0x01
N
START ADDRESS
(!)S TART ADDRESS
N * 32 bits | 0 bits
DATA
(Over)Write N 32bit words from the packet to START ADDRESS
32 bits
32 bits
32 bits
N*32 bits
32 bits
0x02
N
START ADDRESS
N 32 bit words of DATA
(!)S TART ADDRESS
Read/Modify/Write N 32bit words from START ADDRESS + packet to START ADDRESS
32 bits
32 bits
32 bits
N*32 bits
32 bits
OPCODE
N
START ADDRESS
N 32bit words of MASK
(!)S TART ADDRESS
OPCODE
0x03
0x04
0x05
BITWISE OPERATION
AND
OR
XOR
empty