JPEG解码:状态控制模块
状态控制模块主要是对JPEG图像组成的各个部分进行识别,并读取其中的有效信息。主要有以下几个模块:
(1)图像开始(SOI),标识为FFD8,标志一副jpeg图像的开始。
(2)图像识别信息(APP),标识为FFE0,可以读取图像的各种信息,比如XY像素单位,密度等。
(3)量化表(DQT),标识为FFDB,一个AC量化表,一个DC量化表,每个量化表64个数据。
(4)图像信息段(SOF),标识为FFCO,可以读取图片精度,图片高度,图片宽度等信息。
(5)huffman表(DHT),标识为FFC4,读取4个huffman表数据,分别是亮度DC表,亮度AC表,色度DC表,色度AC表。
(6)图像数据段(SOS),标识为FFDA,这个段中存放的是压缩数据。
(7)图像结束(EOI),标识为FFD9,标志一副图片的结束。
数据的排解顺序为:SOI,APP,DQT,DQT,SOF,DHT,DHT,DHT,DHT,SOS,EOI.
其实在DHT段,读取的数据不仅仅是huffman表,还读取了两个表,一个用于存放相同长度的最小编码,另个是最小编码的首地址,对需要解压的数据先判断他的码长,然后再减去相同码长的最小编码可以得到与最小编码的地址差值,然后再加上最小编码的地址就可以得到解码的地址,然后按地址到DHT表中找到对应的数据即可,后面就是huffman解码的事情,本文不讨论。
代码:
`timescale 1ns / 1ps
module fsm(
clk,
rst_n,
data_en,
data,
start,
data_end,
width,
height,
blockwidth,
dqt_en,
dqt_table,
dqt_addr,
dqt_data,
dht_en,
dht_table,
dht_addr,
dht_data,
//
huffman_en,
huffman_table,
huffman_count,
huffman_data,
huffman_startaddr,
//
FF00,
image_en,
//
use_byte,
use_word
);
input clk; //输入时钟信号
input rst_n;//复位信号,低电平有效
input start;//开始解码信号
input data_end;
input data_en;//输入允许信号
input [31:0] data;//输入数据
output [15:0] width;//图片的宽度
output [15:0] height;//图片的高度
output [11:0] blockwidth;//MCU的个数
output dqt_en;//量化表使能信号
output dqt_table;//量化表类型
output [5:0] dqt_addr;//当前值对应的量化表的地址
output [7:0] dqt_data;//量化表的数据
output dht_en;//huffman表使能
output [1:0] dht_table;//huffman表类型
output [7:0] dht_addr;//当前值对应的huffman表的地址
output [7:0] dht_data;//huffman表的数据
//
output huffman_en;//huffman查找表的使能
output [1:0] huffman_table;//查找表的类型
output [3:0] huffman_count;//数据的宽度,0-16
output [15:0] huffman_data;//不同宽度对应的最小的数据
output [7:0] huffman_startaddr;//不同宽度对应的首地址
//
output FF00;//允许去除冗余信息,在SOS段
output image_en;//huffman解码开始信号
//
output use_byte;//传输8位数据
output use_word;//传输16位数据
//--------------------------------------------------------------------------
// FSM
//--------------------------------------------------------------------------
// state Machine Parameter
parameter IDLE = 5'd0;
parameter GetMarker = 5'd1;
parameter ImageData = 5'd2;
// APP Segment
parameter APP_length = 5'd3;
parameter APP_read = 5'd4;
// DQT Segment
parameter DQT_length = 5'd5;
parameter DQT_table = 5'd6;
parameter DQT_read = 5'd7;
// SOF Segment
parameter SOF_length = 5'd8;
parameter SOF_read0 = 5'd9;
parameter SOF_ready = 5'd10;
parameter SOF_readx = 5'd11;
parameter SOF_readcomp = 5'd12;
parameter SOF_makeblock0 = 5'd13;
parameter SOF_makeblock1 = 5'd14;
// DHT Segment
parameter DHT_length = 5'd15;
parameter DHT_table = 5'd16;
parameter DHT_makehm0 = 5'd17;
parameter DHT_makehm1 = 5'd18;
parameter DHT_makehm2 = 5'd19;
parameter DHT_readtable = 5'd20;
// SOS Segment
parameter SOS_length = 5'd21;
parameter SOS_read0 = 5'd22;
parameter SOS_read1 = 5'd23;
parameter SOS_read2 = 5'd24;
parameter SOS_read3 = 5'd25;
parameter SOS_read4 = 5'd26;
reg [4:0] state;
reg [15:0] reg_data;
reg [15:0] width_r;
reg [15:0] height_r;
reg dqt_table_r;
reg [1:0] dht_table_r;
reg [15:0] HmShift;
reg [15:0] HmData;
reg [7:0] HmMax;
reg [7:0] HmCount;
reg HmEnable;
reg [15:0] blockwidth_r;
reg [15:0] blockheight_r;
reg FF00 ;
reg image_r;
always @(posedge clk or negedge rst_n)
begin
if(!rst_n)
begin
state <= IDLE;
reg_data <= 16'd0;
width_r <= 16'd0;
height_r <= 16'd0;
dqt_table_r <= 1'b0;
dht_table_r <= 2'd0;
HmShift <= 16'd0;
HmData <= 16'd0;
HmMax <= 8'd0;
HmCount <= 8'd0;
HmEnable <= 1'b0;
blockwidth_r <= 16'd0;
blockheight_r <= 16'd0;
FF00 <= 1'b0;
image_r <= 1'b0;
end
else begin
case(state)
IDLE:
begin
if(start == 1'b1)
state <= GetMarker;
end
GetMarker:
begin
if(data_en == 1'b1)
case(data[31:16])
16'hFFD8: // SOI Segment
state <= GetMarker;
16'hFFE0: // APP0 Segment
state <= APP_length;
16'hFFDB: // DQT Segment
state <= DQT_length;
16'hFFC0: // SOF0 Segment
state <= SOF_length;
16'hFFC4: // DHT Segment
state <= DHT_length;
16'hFFDA: // SOS Segment
state <= SOS_length;
default:
state <= GetMarker;
endcase
end
APP_length:
begin
if(data_en == 1'b1)
begin
reg_data <= data[31:16] -16'd2;
state <= APP_read;
end
end
APP_read:
begin
if(data_en == 1'b1)
begin
if(reg_data == 16'd1)
state <= GetMarker;
else
reg_data <= reg_data -16'd1;
end
end
DQT_length:
begin
if(data_en == 1'b1)
begin
state <= DQT_table;
reg_data <= data[31:16] -16'd2;
end
end
DQT_table:
begin
if(data_en == 1'b1)
begin
state <= DQT_read;
dqt_table_r <= data[24];
reg_data <= 16'd0;
end
end
DQT_read:
begin
if(data_en == 1'b1)
begin
reg_data <= reg_data +16'd1;
if(reg_data ==63)
state <= GetMarker;
end
end
// SOF0 Segment
SOF_length:
begin
if(data_en == 1'b1)
begin
state <= SOF_read0;
reg_data <= data[31:16];
end
end
SOF_read0:
begin
if(data_en == 1'b1)
state <= SOF_ready;
end
SOF_ready:
begin
if(data_en == 1'b1)
begin
state <= SOF_readx;
height_r <= data[31:16];
blockheight_r <= data[31:16];
end
end
SOF_readx:
begin
if(data_en == 1'b1)
begin
state <= SOF_readcomp;
width_r <= data[31:16];
blockwidth_r <= data[31:16];
reg_data <= 16'd0;
end
end
SOF_readcomp:
begin
if(data_en == 1'b1)
begin
if(reg_data == 9)
state <= SOF_makeblock0;
else
reg_data <= reg_data +16'd1;
end
end
SOF_makeblock0:
begin
state <= SOF_makeblock1;
blockwidth_r <= blockwidth_r +16'd15;
blockheight_r <= blockheight_r +16'd15;
end
SOF_makeblock1:
begin
state <= GetMarker;
blockwidth_r <= blockwidth_r >> 4;
blockheight_r <= blockheight_r >> 4;
end
DHT_length:
begin
if(data_en == 1'b1)
begin
state <= DHT_table;
reg_data <= data[31:16];
end
end
DHT_table:
begin
if(data_en == 1'b1)
begin
state <=DHT_makehm0;
case(data[31:24])
8'h00: dht_table_r <= 2'b00;
8'h10: dht_table_r <= 2'b01;
8'h01: dht_table_r <= 2'b10;
8'h11: dht_table_r <= 2'b11;
endcase
end
HmShift <= 16'h8000;
HmData <= 16'h0000;
HmMax <= 8'h00;
reg_data <= 16'd0;
end
DHT_makehm0:
begin
if(data_en == 1'b1)
begin
state <= DHT_makehm1;
HmCount <= data[31:24];
end
HmEnable <= 1'b0;
end
DHT_makehm1:
begin
state <= DHT_makehm2;
HmMax <= HmMax + HmCount;
end
DHT_makehm2:
begin
if(HmCount != 0)
begin
HmData <= HmData + HmShift;
HmCount <= HmCount -8'd1;
end
else
begin
if(reg_data == 15)
begin
state <= DHT_readtable;
HmCount <= 8'h00;
end else begin
HmEnable <= 1'b1;
state <= DHT_makehm0;
reg_data <= reg_data +16'd1;
end
HmShift <= HmShift >> 1;
end
end
DHT_readtable:
begin
HmEnable <= 1'b0;
if(data_en == 1'b1)
begin
HmCount <= HmCount +8'd1;
if(HmMax == HmCount +1)
begin
state <= GetMarker;
end
end
end
SOS_length:
begin
if(data_en == 1'b1)
begin
state <= SOS_read0;
reg_data <= data[31:16];
FF00 <= 1'b1;
end
end
SOS_read0:
begin
if(data_en == 1'b1)
begin
state <= SOS_read1;
reg_data <= {8'h00,data[31:24]};
end
end
SOS_read1:
begin
if(data_en == 1'b1)
begin
if(reg_data == 1)
state <= SOS_read2;
else
reg_data <= reg_data -16'd1;
end
end
SOS_read2:
begin
if(data_en == 1'b1)
state <= SOS_read3;
end
SOS_read3:
begin
if(data_en == 1'b1)
state <= SOS_read4;
end
SOS_read4:
begin
if(data_en == 1'b1)
begin
state <= ImageData;
image_r <= 1'b1;
end
end
ImageData:
begin
if ( data_end== 1'b1 )
begin
state <= IDLE;
image_r <= 1'b0;
end
end
endcase
end
end
//-------------------------------------------------------------------
assign use_byte = (data_en == 1'b1) & ((state == APP_read) |
(state == DQT_read) |
(state == DQT_table) |
(state == DHT_table) |
(state == DHT_makehm0) |
(state == DHT_readtable) |
(state == SOS_read0) |
(state == SOS_read2) |
(state == SOS_read3) |
(state == SOS_read4) |
(state == SOF_read0) |
(state == SOF_readcomp)
);
assign use_word = (data_en == 1'b1) & ((state == GetMarker) |
(state == APP_length) |
(state == DQT_length) |
(state == DHT_length) |
(state == SOS_length) |
(state == SOS_read1) |
(state == SOF_length) |
(state == SOF_readx) |
(state == SOF_ready)
);
assign width = width_r;
assign height = height_r;
assign blockwidth = blockwidth_r[11:0];
assign dqt_en = (state == DQT_read);
assign dqt_table = dqt_table_r;
assign dqt_addr = reg_data[5:0];
assign dqt_data = data[31:24];
assign dht_en = (state == DHT_readtable);
assign dht_table = dht_table_r;
assign dht_addr = HmCount;
assign dht_data = data[31:24];
assign huffman_en = HmEnable;
assign huffman_table = dht_table_r;
assign huffman_count = reg_data[3:0];
assign huffman_data = HmData;
assign huffman_startaddr = HmMax;
assign image_en=image_r ;
endmodule
GetMarker:
begin
if(data_en == 1'b1)
case(data[31:16])
16'hFFD8: // SOI Segment
state <= GetMarker;
16'hFFE0: // APP0 Segment
state <= APP_length;
16'hFFDB: // DQT Segment
state <= DQT_length;
16'hFFC0: // SOF0 Segment
state <= SOF_length;
16'hFFC4: // DHT Segment
state <= DHT_length;
16'hFFDA: // SOS Segment
state <= SOS_length;
default:
state <= GetMarker;
endcase
end
是对各种不同的头标识的判断 ,然后转入相应的状态执行不同的操作。
DQT_length:
begin
if(data_en == 1'b1)
begin
state <= DQT_table;
reg_data <= data[31:16] -16'd2;
end
end
DQT_table:
begin
if(data_en == 1'b1)
begin
state <= DQT_read;
dqt_table_r <= data[24];
reg_data <= 16'd0;
end
end
DQT_read:
begin
if(data_en == 1'b1)
begin
reg_data <= reg_data +16'd1;
if(reg_data ==63)
state <= GetMarker;
end
end
在量化表读取中,最先读到的是量化表的长度,占2个字节,接下来读到的是量化表的编号,区分DC和AC量化表,占一个字节,再接下来读到的就是64个量化表的数据了,每次读取一个字节,共64字节。
// SOF0 Segment
SOF_length:
begin
if(data_en == 1'b1)
begin
state <= SOF_read0;
reg_data <= data[31:16];
end
end
SOF_read0:
begin
if(data_en == 1'b1)
state <= SOF_ready;
end
SOF_ready:
begin
if(data_en == 1'b1)
begin
state <= SOF_readx;
height_r <= data[31:16];
blockheight_r <= data[31:16];
end
end
SOF_readx:
begin
if(data_en == 1'b1)
begin
state <= SOF_readcomp;
width_r <= data[31:16];
blockwidth_r <= data[31:16];
reg_data <= 16'd0;
end
end
SOF_readcomp:
begin
if(data_en == 1'b1)
begin
if(reg_data == 9)
state <= SOF_makeblock0;
else
reg_data <= reg_data +16'd1;
end
end
SOF_makeblock0:
begin
state <= SOF_makeblock1;
blockwidth_r <= blockwidth_r +16'd15;
blockheight_r <= blockheight_r +16'd15;
end
SOF_makeblock1:
begin
state <= GetMarker;
blockwidth_r <= blockwidth_r >> 4;
blockheight_r <= blockheight_r >> 4;
end
在sof段读取图片的信息:
长度:2个字节
样本位数:1个字节
图片高度:2个字节
图片宽度:2个字节
component的个数,1个字节(若为YCBCR此值为3)
接下来为3组数据,每组数据3个字节,第一个字节代表component,第二个字节代表采样系数,第三个字节代表量化表的编号
DHT_length:
begin
if(data_en == 1'b1)
begin
state <= DHT_table;
reg_data <= data[31:16];
end
end
DHT_table:
begin
if(data_en == 1'b1)
begin
state <=DHT_makehm0;
case(data[31:24])
8'h00: dht_table_r <= 2'b00;
8'h10: dht_table_r <= 2'b01;
8'h01: dht_table_r <= 2'b10;
8'h11: dht_table_r <= 2'b11;
endcase
end
HmShift <= 16'h8000;
HmData <= 16'h0000;
HmMax <= 8'h00;
reg_data <= 16'd0;
end
DHT_makehm0:
begin
if(data_en == 1'b1)
begin
state <= DHT_makehm1;
HmCount <= data[31:24];
end
HmEnable <= 1'b0;
end
DHT_makehm1:
begin
state <= DHT_makehm2;
HmMax <= HmMax + HmCount;
end
DHT_makehm2:
begin
if(HmCount != 0)
begin
HmData <= HmData + HmShift;
HmCount <= HmCount -8'd1;
end
else
begin
if(reg_data == 15)
begin
state <= DHT_readtable;
HmCount <= 8'h00;
end else begin
HmEnable <= 1'b1;
state <= DHT_makehm0;
reg_data <= reg_data +16'd1;
end
HmShift <= HmShift >> 1;
end
end
DHT_readtable:
begin
HmEnable <= 1'b0;
if(data_en == 1'b1)
begin
HmCount <= HmCount +8'd1;
if(HmMax == HmCount +1)
begin
state <= GetMarker;
end
end
end
DHT段中:
长度,2个字节。
huffman表的类型,1个字节。
接下来的16字节中每个字节代表对应长度的代码个数,16字节。
再往下就是huffman表的数据,大小有上面的16字节数据总和决定。
SOS_length:
begin
if(data_en == 1'b1)
begin
state <= SOS_read0;
reg_data <= data[31:16];
FF00 <= 1'b1;
end
end
SOS_read0:
begin
if(data_en == 1'b1)
begin
state <= SOS_read1;
reg_data <= {8'h00,data[31:24]};
end
end
SOS_read1:
begin
if(data_en == 1'b1)
begin
if(reg_data == 1)
state <= SOS_read2;
else
reg_data <= reg_data -16'd1;
end
end
SOS_read2:
begin
if(data_en == 1'b1)
state <= SOS_read3;
end
SOS_read3:
begin
if(data_en == 1'b1)
state <= SOS_read4;
end
SOS_read4:
begin
if(data_en == 1'b1)
begin
state <= ImageData;
image_r <= 1'b1;
end
end
ImageData:
begin
if ( data_end== 1'b1 )
begin
state <= IDLE;
image_r <= 1'b0;
end
end
endcase
end
end
SOS段:
长度,2个字节。
component个数,1个字节。
接下来三组数据,每组2个字节,第一个字节为component ID,第二个字节为使用的huffman表。
3个字节。
接下来就是要解码的数据,启动解码。
assign use_byte = (data_en == 1'b1) & ((state == APP_read) |
(state == DQT_read) |
(state == DQT_table) |
(state == DHT_table) |
(state == DHT_makehm0) |
(state == DHT_readtable) |
(state == SOS_read0) |
(state == SOS_read2) |
(state == SOS_read3) |
(state == SOS_read4) |
(state == SOF_read0) |
(state == SOF_readcomp)
);
assign use_word = (data_en == 1'b1) & ((state == GetMarker) |
(state == APP_length) |
(state == DQT_length) |
(state == DHT_length) |
(state == SOS_length) |
(state == SOS_read1) |
(state == SOF_length) |
(state == SOF_readx) |
(state == SOF_ready)
);
为每个状态下消耗的字节数和字数。