├── Material └── Simulation.png ├── source ├── mac.v ├── shift.v └── conv.v ├── tests ├── shift_testbench.v └── conv_test.v └── README.md /Material/Simulation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/usmanwardag/sobel/HEAD/Material/Simulation.png -------------------------------------------------------------------------------- /source/mac.v: -------------------------------------------------------------------------------- 1 | // Multiply Accumulate Unit 2 | 3 | `timescale 1ns / 1ps 4 | 5 | module mac( 6 | input [7:0] in, 7 | input [7:0] w, 8 | input [7:0] b, 9 | output [15:0] out 10 | ); 11 | 12 | wire [15:0] d; 13 | assign d = w * in; 14 | assign out = d + b; 15 | 16 | endmodule 17 | -------------------------------------------------------------------------------- /tests/shift_testbench.v: -------------------------------------------------------------------------------- 1 | module shift_testbench; 2 | 3 | //Inputs 4 | reg clk; 5 | reg [15:0] data_in; 6 | wire [15:0] data_out; 7 | 8 | shift uut( 9 | .clk(clk), 10 | .data_in(data_in), 11 | .data_out(data_out) 12 | ); 13 | 14 | initial begin 15 | clk = 0; 16 | data_in = 100; 17 | 18 | #100; 19 | 20 | data_in = 10; 21 | #40; 22 | 23 | data_in = 30; 24 | #40; 25 | 26 | data_in = 21; 27 | #40; 28 | 29 | data_in = 110; 30 | #40; 31 | 32 | end 33 | 34 | always #10 clk = ~ clk; 35 | 36 | endmodule -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Implementation of Sobel Filter on Verilog 2 | 3 | The code currently computes convolution of an image with a fixed kernel to find a gradient. By extending the logic to two gradients along x and y axes, and computing the square root of the squared sums, Sobel filter can be implemented. 4 | 5 | The convolution approach has been adopted from [this paper](http://ieeexplore.ieee.org/document/5272559/). 6 | 7 | ## Example 8 | 9 | Assume we have a 5*5 image. 10 | 11 | | 1 | 2 | 3 | 4 | 5 | 12 | | ------------- |:-------------:| -----:|:-------------:| -----:| 13 | | 0 | 1 | 0 | 1 | 0 | 14 | | 1 | 2 | 3 | 4 | 5 | 15 | | 0 | 1 | 0 | 1 | 0 | 16 | | 1 | 2 | 3 | 4 | 5 | 17 | 18 | and a 3*3 kernel 19 | 20 | | 1 | 2 | 1 | 21 | | ------------- |:-------------:| -----:| 22 | | 0 | 0 | 0 | 23 | | 1 | 2 | 1 | 24 | 25 | The output result would be 26 | 27 | | 7 | 12 | 16 | 28 | | ------------- |:-------------:| -----:| 29 | | 4 | 4 | 4 | 30 | | 7 | 12 | 16 | 31 | 32 | ## Simulation 33 | 34 | The result can be verified from the screenshot here. Note that the pxl_out bits are considered only when the valid bit is `1`. 35 | 36 | ![alt tag](Material/Simulation.png) 37 | -------------------------------------------------------------------------------- /tests/conv_test.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | module conv_test; 4 | 5 | // Inputs 6 | reg clk; 7 | reg reset; 8 | reg [7:0] pxl_in; 9 | 10 | // Outputs 11 | wire[15:0] reg_00; wire[15:0] reg_01; wire[15:0] reg_02; wire [15:0] sr_0; 12 | wire[15:0] reg_10; wire[15:0] reg_11; wire[15:0] reg_12; wire [15:0] sr_1; 13 | wire[15:0] reg_20; wire[15:0] reg_21; wire[15:0] reg_22; 14 | 15 | wire [15:0] pxl_out; 16 | wire valid; 17 | 18 | // Instantiate the Unit Under Test (UUT) 19 | conv uut ( 20 | .clk(clk), 21 | .reset(reset), 22 | .pxl_in(pxl_in), 23 | .reg_00(reg_00), 24 | .reg_01(reg_01), 25 | .reg_02(reg_02), 26 | .sr_0(sr_0), 27 | .reg_10(reg_10), 28 | .reg_11(reg_11), 29 | .reg_12(reg_12), 30 | .sr_1(sr_1), 31 | .reg_20(reg_20), 32 | .reg_21(reg_21), 33 | .reg_22(reg_22), 34 | .pxl_out(pxl_out), 35 | .valid(valid) 36 | ); 37 | 38 | initial begin 39 | // Initialize Inputs 40 | clk = 0; 41 | reset = 0; 42 | pxl_in = 0; 43 | 44 | // 5*5 image 45 | #20 pxl_in = 1; #20 pxl_in = 2; #20 pxl_in = 3; #20 pxl_in = 4; #20 pxl_in = 5; 46 | #20 pxl_in = 0; #20 pxl_in = 1; #20 pxl_in = 0; #20 pxl_in = 1; #20 pxl_in = 0; 47 | #20 pxl_in = 1; #20 pxl_in = 2; #20 pxl_in = 3; #20 pxl_in = 4; #20 pxl_in = 5; 48 | #20 pxl_in = 0; #20 pxl_in = 1; #20 pxl_in = 0; #20 pxl_in = 1; #20 pxl_in = 0; 49 | #20 pxl_in = 1; #20 pxl_in = 2; #20 pxl_in = 3; #20 pxl_in = 4; #20 pxl_in = 5; 50 | 51 | end 52 | always #10 clk = ~ clk; 53 | 54 | endmodule -------------------------------------------------------------------------------- /source/shift.v: -------------------------------------------------------------------------------- 1 | // Shifts data by a fixed depth. 2 | // Optimize in future by finding a way to create 2-d arrays 3 | 4 | module shift 5 | ( 6 | input clk, 7 | input [15:0] data_in, 8 | output [15:0] data_out 9 | ); 10 | 11 | // Depth = D = n-k; for now assume n to be 5 12 | parameter D = 2; 13 | 14 | // Define holding register for each bit 15 | reg [D-1:0] hr_0; reg [D-1:0] hr_1; reg [D-1:0] hr_2; reg [D-1:0] hr_3; 16 | reg [D-1:0] hr_4; reg [D-1:0] hr_5; reg [D-1:0] hr_6; reg [D-1:0] hr_7; 17 | reg [D-1:0] hr_8; reg [D-1:0] hr_9; reg [D-1:0] hr_10; reg [D-1:0] hr_11; 18 | reg [D-1:0] hr_12; reg [D-1:0] hr_13; reg [D-1:0] hr_14; reg [D-1:0] hr_15; 19 | 20 | always @ (posedge clk) begin 21 | hr_0 [D-1:0] <= {hr_0[D-2:0], data_in[0]}; 22 | hr_1 [D-1:0] <= {hr_1[D-2:0], data_in[1]}; 23 | hr_2 [D-1:0] <= {hr_2[D-2:0], data_in[2]}; 24 | hr_3 [D-1:0] <= {hr_3[D-2:0], data_in[3]}; 25 | hr_4 [D-1:0] <= {hr_4[D-2:0], data_in[4]}; 26 | hr_5 [D-1:0] <= {hr_5[D-2:0], data_in[5]}; 27 | hr_6 [D-1:0] <= {hr_6[D-2:0], data_in[6]}; 28 | hr_7 [D-1:0] <= {hr_7[D-2:0], data_in[7]}; 29 | hr_8 [D-1:0] <= {hr_8[D-2:0], data_in[8]}; 30 | hr_9 [D-1:0] <= {hr_9[D-2:0], data_in[9]}; 31 | hr_10 [D-1:0] <= {hr_10[D-2:0], data_in[10]}; 32 | hr_11 [D-1:0] <= {hr_11[D-2:0], data_in[11]}; 33 | hr_12 [D-1:0] <= {hr_12[D-2:0], data_in[12]}; 34 | hr_13 [D-1:0] <= {hr_13[D-2:0], data_in[13]}; 35 | hr_14 [D-1:0] <= {hr_14[D-2:0], data_in[14]}; 36 | hr_15 [D-1:0] <= {hr_15[D-2:0], data_in[15]}; 37 | end 38 | 39 | assign data_out[0] = hr_0[D-1]; assign data_out[1] = hr_1[D-1]; 40 | assign data_out[2] = hr_2[D-1]; assign data_out[3] = hr_3[D-1]; 41 | assign data_out[4] = hr_4[D-1]; assign data_out[5] = hr_5[D-1]; 42 | assign data_out[6] = hr_6[D-1]; assign data_out[7] = hr_7[D-1]; 43 | assign data_out[8] = hr_8[D-1]; assign data_out[9] = hr_9[D-1]; 44 | assign data_out[10] = hr_10[D-1]; assign data_out[11] = hr_11[D-1]; 45 | assign data_out[12] = hr_12[D-1]; assign data_out[13] = hr_13[D-1]; 46 | assign data_out[14] = hr_14[D-1]; assign data_out[15] = hr_15[D-1]; 47 | 48 | endmodule -------------------------------------------------------------------------------- /source/conv.v: -------------------------------------------------------------------------------- 1 | `timescale 1ns / 1ps 2 | 3 | module conv( 4 | input clk, 5 | input reset, 6 | input [7:0] pxl_in, 7 | 8 | output [15:0] reg_00, output [15:0] reg_01, output [15:0] reg_02, output[15:0] sr_0, 9 | output [15:0] reg_10, output [15:0] reg_11, output [15:0] reg_12, output[15:0] sr_1, 10 | output [15:0] reg_20, output [15:0] reg_21, output [15:0] reg_22, 11 | output [15:0] pxl_out, 12 | 13 | output valid 14 | ); 15 | 16 | //Define constants 17 | parameter N = 5; //Image columns 18 | parameter M = 5; //Image rows 19 | parameter K = 3; //Kernel size 20 | 21 | // Intermediate wires 22 | wire [15:0] wire_00; wire [15:0] wire_01; wire [15:0] wire_02; 23 | wire [15:0] wire_10; wire [15:0] wire_11; wire [15:0] wire_12; 24 | wire [15:0] wire_20; wire [15:0] wire_21; wire [15:0] wire_22; 25 | 26 | // 3*3 kernel 27 | integer kernel_00 = 1; integer kernel_01 = 2; integer kernel_02 = 1; 28 | integer kernel_10 = 0; integer kernel_11 = 0; integer kernel_12 = 0; 29 | integer kernel_20 = 1; integer kernel_21 = 2; integer kernel_22 = 1; 30 | 31 | // Row : 1 32 | mac mac_00(pxl_in, kernel_00, 0, wire_00); 33 | register r_00(clk, reset, wire_00, reg_00); 34 | 35 | mac mac_01(pxl_in, kernel_01, reg_00, wire_01); 36 | register r_01(clk, reset, wire_01, reg_01); 37 | 38 | mac mac_02(pxl_in, kernel_02, reg_01, wire_02); 39 | register r_02(clk, reset, wire_02, reg_02); 40 | 41 | shift row_1(clk, reg_02, sr_0); 42 | 43 | // Row : 2 44 | mac mac_10(pxl_in, kernel_10, sr_0, wire_10); 45 | register r_10(clk, reset, wire_10, reg_10); 46 | 47 | mac mac_11(pxl_in, kernel_11, reg_10, wire_11); 48 | register r_11(clk, reset, wire_11, reg_11); 49 | 50 | mac mac_12(pxl_in, kernel_12, reg_11, wire_12); 51 | register r_12(clk, reset, wire_12, reg_12); 52 | 53 | shift row_2(clk, reg_12, sr_1); 54 | 55 | // Row : 3 56 | mac mac_20(pxl_in, kernel_20, sr_1, wire_20); 57 | register r_20(clk, reset, wire_20, reg_20); 58 | 59 | mac mac_21(pxl_in, kernel_21, reg_20, wire_21); 60 | register r_21(clk, reset, wire_21, reg_21); 61 | 62 | mac mac_22(pxl_in, kernel_22, reg_21, wire_22); 63 | register r_22(clk, reset, wire_22, reg_22); 64 | 65 | assign pxl_out = reg_22; 66 | 67 | // Valid bit logic 68 | 69 | reg [8:0] counter = 0; 70 | reg temp = 0; 71 | 72 | always @(posedge clk) begin 73 | counter = counter + 1; 74 | 75 | // The logic below needs some revisiting to scale properly 76 | if (counter > ((K-1)*N + (K-1)) && counter < (M*N) + (K-1)) begin 77 | if ((counter - (K-1)) % N > 1) begin 78 | temp <= 1; 79 | end 80 | else 81 | temp <= 0; 82 | end 83 | else 84 | temp <= 0; 85 | end 86 | 87 | assign valid = temp; 88 | 89 | endmodule --------------------------------------------------------------------------------