1. 概述
本设计采用FPGA技术,在FPGA中实现8051单片机的软核,将外部SPI Flash中的代码数据加载到FPGA内部ram,然后复位MC8051,实现外部flash启动MC8051。
2. 系统框图
8051采用Oregano Systems公司开源的MC8051软核。SPI Flash采用W25Q16芯片存储8051的代码程序。系统框图如下:
3. MC8051简介
Oregano Systems的8051单片机采用VHDL语言开发,具有如下特点:
采用完全同步设计
指令集和标准 8051 微控制器完全兼容
指令执行时间为 1~4 个时钟周期,执行性能优于标准 8051 微控制器 8 倍左右
用户可选择定时器/计数器、串行接口单元的数量
新增了特殊功能寄存器用于选择不同的定时器/计数器、串行接口单元
可选择是否使用乘法器(乘法指令 MUL)
可选择是否使用除法器(除法指令 DIV)
可选择是否使用十进制调整功能(十进制调整指令 DA)
I/O 口不复用
内部带 256Bytes RAM
最多可扩展至 64Kbytes 的 ROM 和 64Kbytes 的 RAM
最多可扩展至 64Kbytes 的 ROM 和 64Kbytes 的 RAM
MC8051 IP Core的顶层结构如下图所示:
4. MC8051移植
在mc8051_p.vhd中,更改C_IMPL_N_TMR、C_IMPL_N_SIU、C_IMPL_N_EXT的值可以定义定时器和外部中断的数量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-----------------------------------------------------------------------------
-- Select how many timer/counter units should be implemented
-- Default: 1
constant C_IMPL_N_TMR : integer := 1;
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Select how many serial interface units should be implemented
-- Default: C_IMPL_N_TMR ---(DO NOT CHANGE!)---
constant C_IMPL_N_SIU : integer := C_IMPL_N_TMR;
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Select how many external interrupt-inputs should be implemented
-- Default: C_IMPL_N_TMR ---(DO NOT CHANGE!)---
constant C_IMPL_N_EXT : integer := C_IMPL_N_TMR;
-----------------------------------------------------------------------------
|
在mc8051_p.vhd中,还可以选择需要的指令,可选的指令有MUL/DIV/DA。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
-----------------------------------------------------------------------------
-- Select whether to implement (1) or skip (0) the multiplier
-- Default: 1
constant C_IMPL_MUL : integer := 1;
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Select whether to implement (1) or skip (0) the divider
-- Default: 1
constant C_IMPL_DIV : integer := 1;
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Select whether to implement (1) or skip (0) the decimal adjustment command
-- Default: 1
constant C_IMPL_DA : integer := 1;
-----------------------------------------------------------------------------
|
在FPGA中用内部的ram资源构建一个rom和一个ram以供8051使用,其中rom需为双端口,用于spi_flash_controller加载flash中的程序。
5. SPI Flash简介
SPI Flash芯片是由8192个页组成,每页大小为256字节。16页组成一个扇区,128/256页组成一个块,其结构框图如下:
SPI Flash的读写指令如下:
6. SPI Flash控制器设计
SPI Flash的读写时序如下:其他操作的时序可查看spi flash datasheet。
控制器状态机设计如下:
SPI Flash加载控制逻辑代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
//file name: spi_flash_load.v
//author: shugen.yin
//date: 2017.1.5
//function: load code from spi flash to dram
//log:
module spi_flash_load(
//Global signal
input clk,
input rst_n,
//signal from and to SPI reader
output reg rd_start,
output reg [31:0] rd_addr,
output reg [31:0] rd_length,
input [7:0] rd_data,
input rd_data_valid,
input read_busy,
//signal to mc8051
input [15:0] rdaddress,
output [7:0] data_out,
output reg reset
);
reg [15:0] wraddress;
always @(posedge clk)
if
(rd_data_valid)
wraddress <= wraddress + 1'b1;
else
wraddress <= wraddress;
dpram dpram_inst (
.
clock
( clk ),
.data ( rd_data ),
.rdaddress ( rdaddress ),
.wraddress ( wraddress ),
.wren ( rd_data_valid ),
.q ( data_out )
);
reg [15:0] spi_cnt;
always @(posedge clk)
if
(spi_cnt<=16'h0fff)
spi_cnt <= spi_cnt + 1'b1;
else
spi_cnt <= spi_cnt;
always @(posedge clk)
if
(spi_cnt==1)
begin
rd_start <= 1'b1;
rd_addr <= 'h0;
rd_length<= 'h1000;
end
else
begin
rd_start <= 1'b0;
rd_addr <= rd_addr;
rd_length<= rd_length;
end
always @(posedge clk)
if
(spi_cnt==16'h1000)
reset <= 0;
else
reset <= 1'b1;
endmodule
|
7. MC8051 C语言开发
这里采用Keil uVision4作为开发平台,用到MC8051的I/0和定时器设备,代码设计如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
#include <reg51.h>
sbit P00=P0^0;
char
i=100;
unsigned
char
led=0;
void
Timer0_init(
void
)
{
TMOD = 0x01;
//set timer0 as mode-1
TH0 = 0xee;
TL0 = 0x00;
P00 = 0;
EA = 1;
//enable interrupt
ET0 = 1;
//enable timer0 interrupt
TR0 = 1;
//Trigger Timer0
}
void
main(
void
)
{
Timer0_init();
while
(1){};
}
void
Timer0_int(
void
) interrupt 1
{
TH0 = 0xee;
TL0 = 0x00;
i--;
if
(i<=0)
{
led = ~led;
i = 100;
}
P00 = led;
}
|
编译KEIL工程,得到hex文件:将KEIL生成的hex文件烧写道SPI Flash中。
8. FPGA系统逻辑代码设计
FPGA系统逻辑代码顶层设计如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
|
//file name : top_fpga.v
//data : 2017.1.9
//author : shugen.yin
//function : top of project
//log :
module top_fpga(
//global signal
input clk,
input rst_n,
//led
output [1:0] led
);
//----------------MC8051------------------
wire int0_i;
wire int1_i;
wire all_t0_i;
wire all_t1_i;
wire [7:0] p0_i;
wire [7:0] p1_i;
wire [7:0] p2_i;
wire [7:0] p3_i;
(* keep *)wire [7:0] p0_o;
(* keep *)wire [7:0] p1_o;
(* keep *)wire [7:0] p2_o;
(* keep *)wire [7:0] p3_o;
mc8051_top mc8051_top_inst
(
.clk(clk) ,
// input clk_sig
.reset(spi_load_reset) ,
// input reset_sig
.int0_i(int0_i) ,
// input [0:0] int0_i_sig
.int1_i(int1_i) ,
// input [0:0] int1_i_sig
.all_t0_i(1'b0) ,
// input [0:0] all_t0_i_sig
.all_t1_i(1'b0) ,
// input [0:0] all_t1_i_sig
.p0_i(p0_i) ,
// input [7:0] p0_i_sig
.p1_i(p1_i) ,
// input [7:0] p1_i_sig
.p2_i(p2_i) ,
// input [7:0] p2_i_sig
.p3_i(p3_i) ,
// input [7:0] p3_i_sig
.p0_o(p0_o) ,
// output [7:0] p0_o_sig
.p1_o(p1_o) ,
// output [7:0] p1_o_sig
.p2_o(p2_o) ,
// output [7:0] p2_o_sig
.p3_o(p3_o) ,
// output [7:0] p3_o_sig
.dram_adr_o(spi_load_address) ,
//output address
.dram_data_i(spi_load_data)
//input data
);
assign led = {p0_o[0],~p0_o[0]};
//----------------SPI Flash------------------
wire rd_start;
wire [31:0] rd_addr;
wire [31:0] rd_length;
wire [7:0] rd_data;
wire rd_data_valid;
wire read_busy;
spi_flash_reader spi_flash_reader_inst
(
//global signal
.clk(clk),
.rst_n(rst_n),
//SPI master port
.spi_dclk(flash_clk),
.spi_cs_n(flash_cs_n),
.spi_so(flash_mosi),
.spi_si(flash_miso),
//export port
.rd_start(rd_start),
.rd_addr(rd_addr),
.rd_length(rd_length),
.rd_data(rd_data),
.rd_data_valid(rd_data_valid),
.read_busy(read_busy)
);
wire [15:0] spi_load_address;
wire [7:0] spi_load_data;
wire spi_load_reset;
spi_flash_load spi_flash_load_inst
(
.clk(clk) ,
// input clk_sig
.rst_n(rst_n) ,
// input rst_n_sig
.rd_start(rd_start) ,
// output rd_start_sig
.rd_addr(rd_addr) ,
// output [31:0] rd_addr_sig
.rd_length(rd_length) ,
// output [31:0] rd_length_sig
.rd_data(rd_data) ,
// input [7:0] rd_data_sig
.rd_data_valid(rd_data_valid) ,
// input rd_data_valid_sig
.read_busy(read_busy) ,
// input read_busy_sig
.rdaddress(spi_load_address) ,
// output [11:0] rdaddress_sig
.data_out(spi_load_data),
// output [7:0] data_out_sig
.reset(spi_load_reset)
);
endmodule
|
9. 最终结果
在Quartus II 13.1平台下,编译完成后,将sof文件下载到板卡上,LED交替闪烁。