Escolar Documentos
Profissional Documentos
Cultura Documentos
[http://www.xilinx.com/products/devboards/index.htm]
A modified set of fixed and floating-point packages designed for the IEEE
1076-2006 LRM were utilized for floating point math on the board. The
vhdl-93 version of the VHDL-200X IEEE.Float_Pkg and IEEE.Fixed_Pkg
are currently from the locations listed at the bottom of this section.
The documentation for this project covers most aspects of the design and
implementation. A directory structure and implementation structure is
provided on Page 2. Pages 3 – 13 provide a reference to the RTL blocks of
the project, and Page 14 provide short documentation on all MATLAB
functions. Appendix A provides a introduction to floating-point numbers
and details the MATLAB functions created for working floating-point
numbers.
[http://www.vhdl.org/vhdl-200x/vhdl-200x-ft/packages/files.html]
[Xilinx ISE Application Path/vhdl/src/ieee_proposed].
Implementation This section covers the project HDL implementation on a block by block
basis.
Description
Top Level The top level (Figure 1) of the project is straightforward, with input and
output ports attached to the development board slide switches (ssw),
buttons (btn), and leds (led). A 50 MHz on-board clock is attached to the
(clk) input, BTN South on the board is attached to reset (rst) and finally
rxd and txd connect to the dce_rxd and dce_txd serial pins on the board,
respectively (Table 1).
Implementation
Description
The rs232 unit as a whole transmits and receives 8-bit data over the serial
port asynchronously, adhering to the UART RS-232 Protocol.
Asynchronous means that data can be sent and received at the same
time.
Note: In the following descriptions the slave device refers to the starter
kit board / FPGA UART controller and the master device is defined as the
computer / MATLAB Serial Object.
Implementation
Description
Rs232_Tx The rs232_tx module is connected to the dsend[7:0] input port, the
wstb port, and the txbusy port, as well as the txd and clk ports. The
proper sequence to transmit data is to wait until txbusy has value ‘0’
and then set dsend[7:0] to the byte to be transmitted and raise wstb to
‘1’. Data will start being sent on the rising ed
edge
ge of clk, and will take
10/BAUD_RATE seconds to transmit
transmit, see Figure 3.
Rs232_Rx The rs232_rx module is connect to the dreceive[7:0] output port, the
rxready port, and to the rxd and clk ports. The module contains a
process that checks on the positive edge of clk for a start bit from the
master. When a transmission is detected a register is loaded with the
following eight bits of sent data, and the stop bit is accounted for, and
then the rxready flag raised
raised.. The rxready flag will remain high for one
clock cycle. Outside processes with rxready in their sensitivity list can
detect when data is available and copy it from the dreceive[7:0] port.
Data is guaranteed to be available for only one clock period after
rxready is set high
high, see figure 5.
Implementation
Description
Main This module is connected to all other blocks and is the central unit for
this project (Figure 6). The module consists of a one-hot state machine
inference using soft encoding. The states are listed in Table 3.
stateHoldReg.
acknowledge any 1 Sends ACK byte over serial port
and returns to state loaded in
stateHoldReg.
Table 3 – State descriptions
*number of additional states, for example state_echo also has a second state it goes into: state_echo1.
Useage example:
To read 16 consecutive bytes from the block ram of the FPGA starting
at address 1024.
1) First, break the address into a low byte and high byte. The low byte
is, obviously, 8 bits long, and the high byte is 3 bits long, due to size
limitations of the block ram.
Note: The Spartan XC3S500E FPGA chip has 2048 bytes of available
block ram, and we use an addressing system of 0 to 7FF (0 to 2047
decimal).
>> addr_low_byte = 0;
>> addr_high_byte = 4;
>> count_low_byte = 0;
>> count_high_byte = 16;
>> total_count = 16;
>> fwrite(s, MODE_READ_MEMORY)
>> if (fread(s,1) == ACK)
>> fwrite(s,addr_high_byte);
>> fwrite(s,addr_low_byte);
>> fwrite(s,count_high_byte);
>> fwrite(s,count_low_byte);
>> data = fread(s, total_count);
>> end
>> data
function [data] =
spartan_read_from_memory(s,addr,count)
function spartan_write_to_memory(s,addr,data)
where data is an integer vector, all integers are byte-long (e.g. less
than decimal 256) and it is in row form. E.g. [5,79,35,2,0,255]
Usage example:
>> n1 = double_to_fp32v(-3.1416));
>> n2 = double_to_fp32v(7.0);
26 /
_%&'
state_multiply_matrix To use this mode, floating point numbers need to correctly stored at
offsets in memory starting at MEM_DIV1 for the first matrix and
MEM_DIV2 for the second matrix. Also the resulting matrix will be
stored in MEM_DIV3, so the contents of a matrix-sized area in
MEM_DIV3 must be set to floating-point 0.
Usage example.
This example is stored in demo.m in the matlab directory for
convenience.
>> N = 3;
>> y = randn(N,N)
>> yr = reshape(y’,1,N^2);
>> yfpv = dm_to_fp32m(yr);
>> spartan_write_to_memory(s,MEM_DIV2,yfpv); %loaded
to MEM_DIV2
>> w = zeros(N,N);
>> wr = reshape(w,1,N^2);
>> wfpv = dm_to_fp32m(wr);
>> spartan_write_to_memory(s,MEM_DIV3,wfpv) %clear
MEM_DIV3 (see note)
>> spartan_command(s,MODE_MULTIPLY_MATRIX)
>> fwrite(s,N); %write the N x N size of the
matrices
>> zfpv =
spartan_read_from_memory(s,MEM_DIV3,(N^2)*4);
>> zr = fp32m_to_dm(zfpv);
>> z = reshape(zr,N,N)'; %view the result
Note:
1) MATLAB’s reshape function maps the elements of a matrix into a
vector by column, different than the definition used in Figure 7.
Hence, if using their function, pass the transpose of matrices before
reshaping, and take the transpose of the final result, as in the given
example.
2) The contents at MEM_DIV3 must be cleared to floating-point 0
manually before calculating the matrix multiplication.
3656 /
_%&'
acknowledge This state is entered after anything is received over the serial port if
the state machine is in the idle state. It is used by the MATLAB
functions as a command-received verification.
Command Usage
spartan_load_constants() Loads constants used in above communication
protocol into the MATLAB workspace. Ex. Loads
ACK = hex2dec(‘FE’);
s = spartan_init_protocol() Sets up a serial communication object and opens
the connection with the correct BaudRate,
DataBits, Parity, StopBits and FlowControl
settings for communication with the FPGA.
success = spartan_command(s,command) Sends a command as defined in the constants for
both the FPGA and MATLAB, and checks for
acknowledge. Returns 1 if success, 0 if error.
data = Sends the correct commands to retrieve ‘count’
spartan_read_from_memory(s,addr,count) number of bytes from block ram starting at addr in
memory. addr between 0 and 2047.
spartan_write_to_memory(s,addr,data) Sends the correct commands and data to write
data to block ram. addr between 0 and 2047. data
a 1 x N vector with byte-sized elements, N < 2048.
d = fp32d_to_double(n) Follows the rules as specified by IEEE 754 to
convert between 32-bit floating point numbers
(specified in decimal) to double.
d = fp32v_to_double(v) Converts between 4-byte floating point number
and double. v = [byte1,byte2,byte3,byte4], where
byte1 is first byte in memory operations and most
significant byte.
dv = fp32m_to_dm(f) Converts several 4-byte floating point numbers,
arranged in a vector, end-to-end, similar to the
way they are stored in block ram on FPGA.
v = double_to_fp32v(d) Converts a short double to a 4-byte floating point
representation vector.
v = dm_to_fp32m(d) Converts several short doubles arranged in end-to-
end in a vector, into 4-byte floating point
numbers, similarly arranged.
demo.m Demonstration of writing and reading matrices to
FPGA, and multiplying two matrices on the FPGA.
matrix.m and simulation_address.m Test functions illustrating how matrices are
mapped into memory and multiplication is
performed.
The decimal number −118.625 is encoded using the IEEE 754 system as follows:
1. The sign, the exponent, and the fraction are extracted from the original number. First, because
becaus
the number is negative, the sign bit is "1".
2. Next, the number (without the sign; i.e., unsigned, no two's complement) is converted to binary
notation, giving 1110110.101.
10110.101. The 101 after the binary point has the value 0.625 because it is the
sum of:
1. (2−1) × 1, from the first digit after the binary point
2. (2−2) × 0, from the second digit
3. (2−3) × 1, from the third digit.
3. That binary number is then normalized; that is,, the binary point is moved left, leaving only a 1 to
its left. The number of places it is moved gives the (power of two) exponent: 1110110.101
becomes 1.110110101 × 26. After this process, the first binary digit is always a 1, so it need not
be included in n the encoding. The rest is the part to the right of the binary point, which is then
padded with zeros on the right to make 23 bits in all, which becomes the significand bits in the
encoding: That is, 11011010100000000000000.
4. The exponent is 6. This is enc
encoded
oded by converting it to binary and biasing it (so the most
negative encodable exponent is 0, and all exponents are non non-negative
negative binary numbers). For the
32-bit
bit IEEE 754 format, the bias is +127 and so 6 + 127 = 133. In binary, this is encoded as
10000101.
if (d<0)
S = 1;
else
S = 0;
end
The magnitude of the number is broken into an integer part and a fraction part. E.g. 118.625 has integer
part 118 and fraction part 0.625
d = abs(d);
integer = floor(d);
frac = d - integer;
The integer can immediately be converted to binary using matlab’s builtin function dec2bin(integer).
The fraction is converted using a form of base division. Divide the fraction by base 2, add the interger
part to the binary string and discard, divide remainder by 2, and so on, to a acceptable precision. Read
binfrac = zeros(1,23);
for k = 1:64
binfrac(k) = (floor(frac*2));
frac = 2*frac - floor(2*frac);
end
For example 118.625 -> 1110110 integer part and 1010000000000000000 … fraction part.
Now make a number take binary part from 2:end, e.g. we always know the most significant bit of
integer part will be 1, so we don’t have to keep it. Append fraction part.
Now we have to normalize the number, as described in the Wikipedia entry. Basically we need to move
the decimal dot right or left so there is only 1 to the left of the dot, and keep track of how many places
we move it. So 1110110.1010000000000000000 = 1.1101101010000 , or exponent equals the length of
the integer part – 1, since this is how many places we have to move the dot.
For a number like 0.00101, need to move decimal to the right, and you can see a solution below.
if (integer == 0)
exponent = -find(sbinfrac=='1',1);
f = f(-exponent+1:end);
else
exponent = length(binint) - 1;
end
Then we just combine all the parts into the proper form, keeping only 23 bits of the normalized
integer+fraction part (since the fraction can be made infinetly precise).
e = exponent + exponent_base;
v = [dec2bin(S,1) dec2bin(e,8) f(1:23)];
Credits
Credit for vhdl and MATLAB design goes to David Freiberger and
Tyler Jones.
Credit (last but not least) goes to Dr. Talarico for all his effort in
helping us to learn VHDL and for his helpful comments on design,
coming from his experience in the industry.
technology.ewu.edu
ieee.ewu.edu
Tools used in this project include the Xilinx ISE Design Suite 10.1
(WebPack), which is freely available to students and researchers,
MATLAB, which is not freely available, Programmers Notepad for
editing and viewing C, VHDL, Verilog, and MATLAB files all at once
with nice syntax highlighting, and Adobe Photoshop for desiging this
report.