HDL Coder

2D FIR Filter

This example shows how to generate HDL code from a MATLAB® design that unsharps an image using 2D FIR filtering.

Algorithm

The image unsharp technique filters images with a 2D unsharp kernel so the contrast of the image is enhanced. The following MATLAB code shows the effects of unsharping.

image_in = imread('mlhdlc_cameraman.tif');
H = fspecial('unsharp');
filt_image = imfilter(image_in,H);
figure;
subplot(1,2,1);
imshow(image_in);
title('Original');
subplot(1,2,2);
imshow(filt_image);
title('Sharpened');

MATLAB Design

The following example shows how to implement the image processing algorithm for HDL code generation.

design_name = 'mlhdlc_2DFIR';
testbench_name = 'mlhdlc_2DFIR_tb';

Let us take a look at the MATLAB design

type(design_name);
%#codegen
function [pixel_out] = mlhdlc_2DFIR(pixel_in)
%
% The 2D FIR algorithm maintains three line buffers. Each iteration the
% input pixel is pushed into the current line buffer that is being written to.
% The control logic rotates between these three buffers when it reaches the
% column boundary.
%
% Each buffer is followed by a shift register and data at the current
% column index is pushed into the shift register.
%
% At each iteration a 3x3 kernel of pixels is formed from the pixel input, shift
% registers and line buffer outputs.
%
% The kernel is multiplied by a 3x3 filter coefficient mask and the sum of
% the resultant values is computed as the pixel output.

nRows = 260;
nCols = 260;
mask = [-0.1667 -0.6667 -0.1667 -0.6667 4.3333 -0.6667 -0.1667 -0.6667 -0.1667];

persistent row_count;
persistent col_count;
persistent t_minus_1_pixel;
persistent t_minus_2_pixel;
persistent t_minus_1_memrow1;
persistent t_minus_2_memrow1;
persistent t_minus_1_memrow2;
persistent t_minus_2_memrow2;
persistent t_minus_1_memrow3;
persistent t_minus_2_memrow3;
persistent mem_row_idx;
persistent mem_row1;
persistent mem_row2;
persistent mem_row3;

if isempty(t_minus_1_memrow3)
    t_minus_1_memrow3 = 0;
    t_minus_2_memrow3 = 0;
    t_minus_1_memrow2 = 0;
    t_minus_2_memrow2 = 0;
    t_minus_1_memrow1 = 0;
    t_minus_2_memrow1 = 0;
    row_count = 1;
    col_count = 1;
    t_minus_1_pixel = 0;
    t_minus_2_pixel = 0;
    mem_row_idx = 1;
    mem_row1 = zeros(1,nCols);
    mem_row2 = zeros(1,nCols);
    mem_row3 = zeros(1,nCols);
end

row_count_r=row_count;
col_count_r=col_count;
t_minus_1_pixel_r=t_minus_1_pixel;
t_minus_2_pixel_r=t_minus_2_pixel;
t_minus_1_memrow1_r=t_minus_1_memrow1;
t_minus_2_memrow1_r=t_minus_2_memrow1;
t_minus_1_memrow2_r=t_minus_1_memrow2;
t_minus_2_memrow2_r=t_minus_2_memrow2;
t_minus_1_memrow3_r=t_minus_1_memrow3;
t_minus_2_memrow3_r=t_minus_2_memrow3;
mem_row_idx_r = mem_row_idx;

write_col_idx = col_count_r;

current_mem_row1_data = mem_row1(write_col_idx);
current_mem_row2_data = mem_row2(write_col_idx);
current_mem_row3_data = mem_row3(write_col_idx);

if mem_row_idx_r==1
    top_row= [t_minus_2_memrow2_r t_minus_1_memrow2_r current_mem_row2_data];
    middle_row= [t_minus_2_memrow3_r t_minus_1_memrow3_r current_mem_row3_data];
elseif mem_row_idx_r==2
    top_row= [t_minus_2_memrow3_r t_minus_1_memrow3_r current_mem_row3_data];
    middle_row= [t_minus_2_memrow1_r t_minus_1_memrow1_r current_mem_row1_data];
else
    top_row= [t_minus_2_memrow1_r t_minus_1_memrow1_r current_mem_row1_data];
    middle_row= [t_minus_2_memrow2_r t_minus_1_memrow2_r current_mem_row2_data];
end

bottom_row = [ t_minus_2_pixel_r t_minus_1_pixel_r pixel_in];

kernel = [top_row middle_row bottom_row];
if col_count_r>=3 && row_count_r>=3
    %pixel_out=sum(operand.*mask);
    
    m1 = kernel(1) * mask(1);
    m2 = kernel(2) * mask(2);
    m3 = kernel(3) * mask(3);
    m4 = kernel(4) * mask(4);
    m5 = kernel(5) * mask(5);
    m6 = kernel(6) * mask(6);
    m7 = kernel(7) * mask(7);
    m8 = kernel(8) * mask(8);
    m9 = kernel(9) * mask(9);
    
    % tree of adders
    s1 = m1 + m2;
    s2 = m3 + m4;
    s3 = m5 + m6;
    s4 = m7 + m8;    
    s21 = s1 + s2;
    s22 = s3 + s4;    
    s31 = s21 + s22;    
    pixel_out = s31 + m9;            
else
    pixel_out=0;
end


if mem_row_idx_r==1
    mem_row1_write_data = pixel_in;
    mem_row2_write_data = current_mem_row2_data;
    mem_row3_write_data = current_mem_row3_data;
elseif mem_row_idx_r==2
    mem_row1_write_data = current_mem_row1_data;
    mem_row2_write_data = pixel_in;
    mem_row3_write_data = current_mem_row3_data;
else
    mem_row1_write_data = current_mem_row1_data;
    mem_row2_write_data = current_mem_row2_data;
    mem_row3_write_data = pixel_in;
end

mem_row1(write_col_idx)=mem_row1_write_data;
mem_row2(write_col_idx)=mem_row2_write_data;
mem_row3(write_col_idx)=mem_row3_write_data;

if  col_count_r==nCols
    %toggle memrow
    if mem_row_idx_r ==1;
        mem_row_idx=2;
    elseif mem_row_idx_r ==2;
        mem_row_idx=3;
    else
        mem_row_idx=1;
    end
end
t_minus_1_pixel = pixel_in;
t_minus_2_pixel = t_minus_1_pixel_r;

t_minus_1_memrow1=current_mem_row1_data;
t_minus_2_memrow1=t_minus_1_memrow1_r;

t_minus_1_memrow2=current_mem_row2_data;
t_minus_2_memrow2=t_minus_1_memrow2_r;

t_minus_1_memrow3=current_mem_row3_data;
t_minus_2_memrow3=t_minus_1_memrow3_r;

if col_count_r+1<=nCols
    col_count = col_count_r+1;
else
    col_count =  1;
    if row_count_r<nRows
        row_count = row_count_r+1;
    else
        row_count = 1;
    end
end

end
type(testbench_name);
clear mlhdlc_2DFIR;
TOL = 1e-6;

% Read the image
image_in = double(imread('mlhdlc_cameraman.tif'));
H = fspecial('unsharp');

% Pad the image
[rows pixels] = size(image_in);
pad_vert_im = [ zeros(1,pixels);zeros(1,pixels);image_in;...
    zeros(1,pixels);zeros(1,pixels)];
pad_horiz_im = [ zeros(rows+4,2) pad_vert_im zeros(rows+4,2)];

% Reshape the image as a vector
[rows pixels] = size(pad_horiz_im);
image_vector_length = rows*pixels;
image_in_vector = reshape(pad_horiz_im',1,image_vector_length);

% Pre-allocating y for simulation performance
y = zeros(1,length(image_in_vector));

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% Streaming loop calling the design
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for i = 1:length(image_in_vector)
    
    y(i) = mlhdlc_2DFIR(image_in_vector(i));
end

% Reshape the output back to a 2D matrix
image_out = reshape(y,pixels,rows)';


filt_image = imfilter(image_in,H,0);
err = filt_image-image_out(4:end-1,4:end-1);
err = (err > TOL) .* err;

figure('Name', [mfilename, '_plot']);
subplot(1,2,1);
imshow(int8(image_out(4:end-1,4:end-1)));title('HDL Output');
subplot(1,2,2);
imshow(err);title('Difference');

Simulate the Design

It is a good practice to simulate the design with the testbench prior to code generation to make sure there are no runtime errors.

mlhdlc_2DFIR_tb

Setup for the Example

Executing the following lines of code copies the necessary example files into a temporary folder

mlhdlc_demo_dir = fullfile(matlabroot, 'toolbox', 'hdlcoder', 'hdlcoderdemos', 'matlabhdlcoderdemos');
mlhdlc_temp_dir = [tempdir 'mlhdlc_sobel'];

% create a temporary folder and copy the MATLAB files
[~, ~, ~] = rmdir(mlhdlc_temp_dir, 's');
mkdir(mlhdlc_temp_dir);
cd(mlhdlc_temp_dir);

% copy the design files to the temporary directory
copyfile(fullfile(mlhdlc_demo_dir, [design_name,'.m*']), mlhdlc_temp_dir);
copyfile(fullfile(mlhdlc_demo_dir, [testbench_name,'.m*']), mlhdlc_temp_dir);

Create a New HDL Coder™ Project

coder -hdlcoder -new mlhdlc_2dfir_prj

Next, add the file 'mlhdlc_2DFIR.m' to the project as the MATLAB Function and 'mlhdlc_2DFIR_tb.m' as the MATLAB Test Bench.

You can refer to Getting Started with MATLAB to HDL Workflow tutorial for a more complete tutorial on creating and populating MATLAB HDL Coder projects.

Run Fixed-Point Conversion and HDL Code Generation

Launch HDL Advisor and right click on the 'Code Generation' step and choose the option 'Run to selected task' to run all the steps from the beginning through the HDL code generation.

Examine the generated HDL code by clicking on the hyperlinks in the Code Generation Log window.

Enable Distributed Pipelining Option

To improve clock frequency of the synthesized circuit you can use the distributed pipelining option to pipeline the multipliers inferred in the design.

  1. Choose the option 'Output Pipeline: 10'

  2. Enable the option 'Distribute Pipeline Registers'

  3. Reset the 'Code Generation' task

  4. Rerun the code generation and synthesis steps and examine the synthesis results.

Clean up the Generated Files

You can run the following commands to clean up the temporary project folder.

mlhdlc_demo_dir = fullfile(matlabroot, 'toolbox', 'hdlcoder', 'hdlcoderdemos', 'matlabhdlcoderdemos');
mlhdlc_temp_dir = [tempdir 'mlhdlc_sobel'];
clear mex;
cd (mlhdlc_demo_dir);
rmdir(mlhdlc_temp_dir, 's');