Sin Lookup Table

RAM-Based Look-Up Table (LUT)

In this assignment you will use a RAM to implement a trigonemetric LUT function. The RAM will be initialized from a data file containing values of y=255*sin(pi*x/512). The values of x shall be 8-bit unsigned integers in the range 0 to 255, and the values of y shall be rounded off to 8-bit unsigned integers in the range 0 to 255.

The 8-bit input/output values can be interpreted as unsigned integers or as fixed-point non-negative fractional values 0, 1/255, 2/255, and so on up to 255/255. Under the fixed-point interpretation, the function becomes y = sin(pi*x/2). The function is written differently since we changed the numerical interpretation, but the binary values are unchanged.

Obtaining LUT Samples

The LUT data can be generated using Matlab or another high-level programming language. In this exercise we will use Octave (a free clone of Matlab), and the commands should work precisely the same in Matlab.

First, use Octave/Matlab to create and verify a sample vector:

x = 0:255;
y = round(255*sin(pi*x/512));
plot(x,y)

These lines should produce a plot showing one quarter-wave of a sin function. It should rise from a minimum of 0 at x=0 up to a maximum of 255 where x=255.

Next, you should convert the samples to hexadecimal form and save them to a data file:

fid=fopen('sin_table.dat','w');  % Open file for writing
for idx=1:256
   fprintf(fid,'%02x\n',y(idx));  % Write hex data, one byte per line
end
fclose(fid); % close file

In a terminal, verify the contents of sin_table.dat:

less sin_table.dat

You should see a column of hexadecimal bytes like this:

00
02
03
05
06
08
09
0b
0d
0e
10
11
13
14
16
17
19
1b
1c
1e
1f

Creating the LUT in Verilog

Create a RAM module based on the previous assignment. Name the module sin_table. The WIDTH should be 8, the DEPTH should be 256, and the ADR_WIDTH should be 8. In the module, add an initial assignment like this:

initial begin
   $readmemh("sin_table.dat", ram, 0, 255);
end

This will initialize the ram with data from sin_table.dat.

For this application, we do not need to mofidy the RAM contents. Comment out or delete lines pertaining to write operations. With no write operations, this RAM becomes a ROM.

Verifying the Design

We can verify the sin table in Verilog using variables of type real and mathematical system tasks. A real variable is not synthesizeable but is very useful to confirm signal calculations. Create an always scope for stimulus and reporting:

reg [8:0] adr;
real      sin_x, sin_y;

// Get a new random adr every clk cycle:
always @(posedge clk) begin
   adr = $random();
end

// When dout changes, display it:
always @(dout) begin
   sin_x = adr/255.0;
   sin_y = dout/255.0;
   $write("adr %3d  dout %3d: sin(%1.3f)=%1.3f, should be %1.3f",adr,dout,sin_x, sin_y, $sin(3.14159*sin_x/2));
   $fwrite(fid,"adr %3d  dout %3d: sin(%1.3f)=%1.3f, should be %1.3f",adr,dout,sin_x, sin_y, $sin(3.14159*sin_x/2));
end

Assigned Tasks

This is a simulation-only assignment, no build.

  1. Using Octave or Matlab, create the sample file sin_table.dat and save it in the assignment directory (not in src).
  2. Complete the sin_table module and testbench.
  3. Verify the function in simulation. You may see a slight difference between the sin_table output and the $sin() result. This difference is due to round-off error. The first two significant figures should match.

Turn in your work using git:

git add src/*.v *.txt sin_table.dat Makefile
git commit . -m "Complete"
git push origin master

Indicate on Canvas that your assignment is done.