umtrx: updated many cores and top level, dsp still needs proper strobes

This commit is contained in:
Josh Blum
2014-04-15 14:35:14 -07:00
parent 7bdb3ac8c3
commit ca67428214
51 changed files with 2866 additions and 1940 deletions

View File

@@ -1,5 +1,5 @@
#
# Copyright 2010 Ettus Research LLC
# Copyright 2010-2012 Ettus Research LLC
#
##################################################
@@ -55,4 +55,8 @@ settings_bus_16LE.v \
atr_controller16.v \
fifo_to_wb.v \
gpio_atr.v \
user_settings.v \
settings_fifo_ctrl.v \
simple_spi_core.v \
simple_i2c_core.v \
))

View File

@@ -27,7 +27,7 @@ module double_buffer_tb();
reg src_rdy_i = 0;
wire dst_rdy_o;
wire dst_rdy_i = 1;
wire dst_rdy_i = 0;
wire [35:0] data_o;
reg [35:0] data_i;
@@ -46,9 +46,9 @@ module double_buffer_tb();
.data_i(data_i), .src_rdy_i(src_rdy_i), .dst_rdy_o(dst_rdy_o),
.data_o(data_o), .src_rdy_o(src_rdy_o), .dst_rdy_i(dst_rdy_i));
dspengine_16to8 dspengine_16to8
dspengine_8to16 #(.HEADER_OFFSET(1)) dspengine_8to16
(.clk(clk),.reset(rst),.clear(0),
.set_stb(set_stb), .set_addr(0), .set_data({13'h0,1'b1,18'h00400}),
.set_stb(set_stb), .set_addr(0), .set_data(1),
.access_we(access_we), .access_stb(access_stb), .access_ok(access_ok), .access_done(access_done),
.access_skip_read(access_skip_read), .access_adr(access_adr), .access_len(access_len),
.access_dat_i(buf_to_dsp), .access_dat_o(dsp_to_buf));
@@ -69,11 +69,13 @@ module double_buffer_tb();
@(posedge clk);
@(posedge clk);
@(posedge clk);
/*
// Passthrough
$display("Passthrough");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'hFFFFFFFF};
data_i <= { 2'b00,1'b0,1'b1,32'h01234567};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'hFFFFFFFF};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h04050607};
@(posedge clk);
@@ -86,16 +88,18 @@ module double_buffer_tb();
repeat (5)
@(posedge clk);
*/
$display("Enabled");
set_stb <= 1;
@(posedge clk);
set_stb <= 0;
/*
@(posedge clk);
$display("Non-IF Data Passthrough");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'hC0000000};
data_i <= { 2'b00,1'b0,1'b1,32'h89acdef0};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'hC0000000};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h14151617};
@(posedge clk);
@@ -111,7 +115,9 @@ module double_buffer_tb();
$display("No StreamID, No Trailer, Even");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'h0000FFFF};
data_i <= { 2'b00,1'b0,1'b1,32'hAAAAAAAA};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h0000FFFF};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h01000200};
@(posedge clk);
@@ -139,7 +145,9 @@ module double_buffer_tb();
$display("No StreamID, No Trailer, Odd");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'h0000FFFF};
data_i <= { 2'b00,1'b0,1'b1,32'hBBBBBBBB};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h0000FFFF};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h11001200};
@(posedge clk);
@@ -159,30 +167,59 @@ module double_buffer_tb();
while(~dst_rdy_o)
@(posedge clk);
*/
/*
$display("No StreamID, Trailer, Even");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'h0400FFFF};
data_i <= { 2'b00,1'b0,1'b1,32'hCCCCCCCC};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h21002200};
data_i <= { 2'b00,1'b0,1'b0,32'h0400FFFF};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h23002400};
data_i <= { 2'b00,1'b0,1'b0,32'h21222324};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h25262728};
src_rdy_i <= 0;
@(posedge clk);
src_rdy_i <= 1;
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h25002600};
data_i <= { 2'b00,1'b0,1'b0,32'h292a2b2c};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h27002800};
data_i <= { 2'b00,1'b0,1'b0,32'h2d2e2f30};
@(posedge clk);
data_i <= { 2'b00,1'b1,1'b0,32'h29002a00};
data_i <= { 2'b00,1'b1,1'b0,32'hDEADBEEF};
@(posedge clk);
src_rdy_i <= 0;
@(posedge clk);
*/
while(~dst_rdy_o)
@(posedge clk);
/*
$display("No StreamID, Trailer, Odd");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'hDDDDDDDD};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h0400FFFF};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h21222324};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h25262728};
src_rdy_i <= 0;
@(posedge clk);
src_rdy_i <= 1;
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h292a2b2c};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'h2d2e2f30};
@(posedge clk);
data_i <= { 2'b00,1'b1,1'b0,32'hDEBDBF0D};
@(posedge clk);
src_rdy_i <= 0;
@(posedge clk);
*/
while(~dst_rdy_o)
@(posedge clk);
/*
$display("No StreamID, Trailer, Odd");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'h0400FFFF};
@@ -226,23 +263,45 @@ module double_buffer_tb();
while(~dst_rdy_o)
@(posedge clk);
*/
$display("StreamID, Trailer, Odd");
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'h1400FFFF};
data_i <= { 2'b00,1'b0,1'b1,32'hABCDEF98};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha100a200};
data_i <= { 2'b00,1'b0,1'b0,32'h1c034567};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha0a1a2a3};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha4a5a6a7};
// src_rdy_i <= 0;
// @(posedge clk);
// src_rdy_i <= 1;
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha8a9aaab};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'hacadaeaf};
@(posedge clk);
data_i <= { 2'b00,1'b1,1'b0,32'hdeadbeef};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha300a400};
src_rdy_i <= 0;
@(posedge clk);
src_rdy_i <= 1;
data_i <= { 2'b00,1'b0,1'b1,32'hABCDEF98};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha500a600};
data_i <= { 2'b00,1'b0,1'b0,32'h1c034567};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha700a800};
data_i <= { 2'b00,1'b0,1'b0,32'ha0a1a2a3};
@(posedge clk);
data_i <= { 2'b00,1'b1,1'b0,32'hbbb0bbb0};
data_i <= { 2'b00,1'b0,1'b0,32'ha4a5a6a7};
// src_rdy_i <= 0;
// @(posedge clk);
// src_rdy_i <= 1;
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'ha8a9aaab};
@(posedge clk);
data_i <= { 2'b00,1'b0,1'b0,32'hacadaeaf};
@(posedge clk);
data_i <= { 2'b00,1'b1,1'b0,32'hdeadbeef};
@(posedge clk);
src_rdy_i <= 0;
@(posedge clk);

View File

@@ -1,5 +1,5 @@
//
// Copyright 2011 Ettus Research LLC
// Copyright 2011-2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
@@ -22,16 +22,17 @@
// the system or dsp clock on the output side
module settings_bus_crossclock
#(parameter FLOW_CTRL=0)
(input clk_i, input rst_i, input set_stb_i, input [7:0] set_addr_i, input [31:0] set_data_i,
input clk_o, input rst_o, output set_stb_o, output [7:0] set_addr_o, output [31:0] set_data_o);
input clk_o, input rst_o, output set_stb_o, output [7:0] set_addr_o, output [31:0] set_data_o, input blocked);
wire full, empty;
fifo_xlnx_16x40_2clk settings_fifo
(.rst(rst_i),
.wr_clk(clk_i), .din({set_addr_i,set_data_i}), .wr_en(set_stb_i & ~full), .full(full),
.rd_clk(clk_o), .dout({set_addr_o,set_data_o}), .rd_en(~empty), .empty(empty));
.rd_clk(clk_o), .dout({set_addr_o,set_data_o}), .rd_en(set_stb_o), .empty(empty));
assign set_stb_o = ~empty;
assign set_stb_o = ~empty & (~blocked | ~FLOW_CTRL);
endmodule // settings_bus_crossclock

View File

@@ -0,0 +1,395 @@
//
// Copyright 2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// A settings and readback bus controlled via fifo36 interface
module settings_fifo_ctrl
#(
parameter XPORT_HDR = 1, //extra transport hdr line
parameter PROT_DEST = 0, //protocol framer destination
parameter PROT_HDR = 1, //needs a protocol header?
parameter ACK_SID = 0 //stream ID for packet ACK
)
(
//clock and synchronous reset for all interfaces
input clock, input reset, input clear,
//current system time
input [63:0] vita_time,
//ready signal for multiple peripherals
input perfs_ready,
//input fifo36 interface control
input [35:0] in_data, input in_valid, output in_ready,
//output fifo36 interface status
output [35:0] out_data, output out_valid, input out_ready,
//32-bit settings bus outputs
output strobe, output [7:0] addr, output [31:0] data,
//16X 32-bit inputs for readback
input [31:0] word00,
input [31:0] word01,
input [31:0] word02,
input [31:0] word03,
input [31:0] word04,
input [31:0] word05,
input [31:0] word06,
input [31:0] word07,
input [31:0] word08,
input [31:0] word09,
input [31:0] word10,
input [31:0] word11,
input [31:0] word12,
input [31:0] word13,
input [31:0] word14,
input [31:0] word15,
//debug output
output [31:0] debug
);
wire reading = in_valid && in_ready;
wire writing = out_valid && out_ready;
//------------------------------------------------------------------
//-- The command fifo:
//-- Stores an individual register access command per line.
//------------------------------------------------------------------
wire [63:0] in_command_ticks, out_command_ticks;
wire [31:0] in_command_hdr, out_command_hdr;
wire [31:0] in_command_data, out_command_data;
wire in_command_has_time, out_command_has_time;
wire command_fifo_full, command_fifo_empty;
wire command_fifo_read, command_fifo_write;
shortfifo #(.WIDTH(129)) command_fifo (
.clk(clock), .rst(reset), .clear(clear),
.datain({in_command_ticks, in_command_hdr, in_command_data, in_command_has_time}),
.dataout({out_command_ticks, out_command_hdr, out_command_data, out_command_has_time}),
.write(command_fifo_write), .full(command_fifo_full), //input interface
.empty(command_fifo_empty), .read(command_fifo_read) //output interface
);
//------------------------------------------------------------------
//-- The result fifo:
//-- Stores an individual result of a command per line.
//------------------------------------------------------------------
wire [31:0] in_result_hdr, out_result_hdr;
wire [31:0] in_result_data, out_result_data;
wire result_fifo_full, result_fifo_empty;
wire result_fifo_read, result_fifo_write;
shortfifo #(.WIDTH(64)) result_fifo (
.clk(clock), .rst(reset), .clear(clear),
.datain({in_result_hdr, in_result_data}),
.dataout({out_result_hdr, out_result_data}),
.write(result_fifo_write), .full(result_fifo_full), //input interface
.empty(result_fifo_empty), .read(result_fifo_read) //output interface
);
//------------------------------------------------------------------
//-- Input state machine:
//-- Read input packet and fill a command fifo entry.
//------------------------------------------------------------------
localparam READ_LINE0 = 0;
localparam VITA_HDR = 1;
localparam VITA_SID = 2;
localparam VITA_CID0 = 3;
localparam VITA_CID1 = 4;
localparam VITA_TSI = 5;
localparam VITA_TSF0 = 6;
localparam VITA_TSF1 = 7;
localparam READ_HDR = 8;
localparam READ_DATA = 9;
localparam WAIT_EOF = 10;
localparam STORE_CMD = 11;
localparam START_STATE = (XPORT_HDR)? READ_LINE0 : VITA_HDR;
reg [4:0] in_state;
//holdover from current read inputs
reg [31:0] in_data_reg, in_hdr_reg;
reg [63:0] in_ticks_reg;
wire has_sid = in_data[28];
wire has_cid = in_data[27];
wire has_tsi = in_data[23:22] != 0;
wire has_tsf = in_data[21:20] != 0;
reg has_sid_reg, has_cid_reg, has_tsi_reg, has_tsf_reg;
assign in_ready = (in_state < STORE_CMD);
assign command_fifo_write = (in_state == STORE_CMD);
assign in_command_ticks = in_ticks_reg;
assign in_command_data = in_data_reg;
assign in_command_hdr = in_hdr_reg;
assign in_command_has_time = has_tsf_reg;
always @(posedge clock) begin
if (reset) begin
in_state <= START_STATE;
end
else begin
case (in_state)
READ_LINE0: begin
if (reading) in_state <= VITA_HDR;
end
VITA_HDR: begin
if (reading) begin
if (has_sid) in_state <= VITA_SID;
else if (has_cid) in_state <= VITA_CID0;
else if (has_tsi) in_state <= VITA_TSI;
else if (has_tsf) in_state <= VITA_TSF0;
else in_state <= READ_HDR;
end
has_sid_reg <= has_sid;
has_cid_reg <= has_cid;
has_tsi_reg <= has_tsi;
has_tsf_reg <= has_tsf;
end
VITA_SID: begin
if (reading) begin
if (has_cid_reg) in_state <= VITA_CID0;
else if (has_tsi_reg) in_state <= VITA_TSI;
else if (has_tsf_reg) in_state <= VITA_TSF0;
else in_state <= READ_HDR;
end
end
VITA_CID0: begin
if (reading) in_state <= VITA_CID1;
end
VITA_CID1: begin
if (reading) begin
if (has_tsi_reg) in_state <= VITA_TSI;
else if (has_tsf_reg) in_state <= VITA_TSF0;
else in_state <= READ_HDR;
end
end
VITA_TSI: begin
if (reading) begin
if (has_tsf_reg) in_state <= VITA_TSF0;
else in_state <= READ_HDR;
end
end
VITA_TSF0: begin
if (reading) in_state <= VITA_TSF1;
in_ticks_reg[63:32] <= in_data;
end
VITA_TSF1: begin
if (reading) in_state <= READ_HDR;
in_ticks_reg[31:0] <= in_data;
end
READ_HDR: begin
if (reading) in_state <= READ_DATA;
in_hdr_reg <= in_data[31:0];
end
READ_DATA: begin
if (reading) in_state <= (in_data[33])? STORE_CMD : WAIT_EOF;
in_data_reg <= in_data[31:0];
end
WAIT_EOF: begin
if (reading && in_data[33]) in_state <= STORE_CMD;
end
STORE_CMD: begin
if (~command_fifo_full) in_state <= START_STATE;
end
endcase //in_state
end
end
//------------------------------------------------------------------
//-- Command state machine:
//-- Read a command fifo entry, act on it, produce result.
//------------------------------------------------------------------
localparam LOAD_CMD = 0;
localparam EVENT_CMD = 1;
reg cmd_state;
reg [31:0] rb_data;
reg [63:0] command_ticks_reg;
reg [31:0] command_hdr_reg;
reg [31:0] command_data_reg;
reg [63:0] vita_time_reg;
always @(posedge clock)
vita_time_reg <= vita_time;
wire late;
`ifndef FIFO_CTRL_NO_TIME
time_compare time_compare(
.time_now(vita_time_reg), .trigger_time(command_ticks_reg), .late(late));
`else
assign late = 1;
`endif
//action occurs in the event state and when there is fifo space (should always be true)
//the third condition is that all peripherals in the perfs signal are ready/active high
//the fourth condition is that is an event time has been set, action is delayed until that time
wire time_ready = (out_command_has_time)? late : 1;
wire action = (cmd_state == EVENT_CMD) && ~result_fifo_full && perfs_ready && time_ready;
assign command_fifo_read = action;
assign result_fifo_write = action;
assign in_result_hdr = command_hdr_reg;
assign in_result_data = rb_data;
always @(posedge clock) begin
if (reset) begin
cmd_state <= LOAD_CMD;
end
else begin
case (cmd_state)
LOAD_CMD: begin
if (~command_fifo_empty) cmd_state <= EVENT_CMD;
command_ticks_reg <= out_command_ticks;
command_hdr_reg <= out_command_hdr;
command_data_reg <= out_command_data;
end
EVENT_CMD: begin // poking and peeking happens here!
if (action || clear) cmd_state <= LOAD_CMD;
end
endcase //cmd_state
end
end
//------------------------------------------------------------------
//-- assign to settings bus interface
//------------------------------------------------------------------
reg strobe_reg;
assign strobe = strobe_reg;
assign data = command_data_reg;
assign addr = command_hdr_reg[7:0];
wire poke = command_hdr_reg[8];
always @(posedge clock) begin
if (reset || clear) strobe_reg <= 0;
else strobe_reg <= action && poke;
end
//------------------------------------------------------------------
//-- readback mux
//------------------------------------------------------------------
always @(posedge clock) begin
case (out_command_hdr[3:0])
0 : rb_data <= word00;
1 : rb_data <= word01;
2 : rb_data <= word02;
3 : rb_data <= word03;
4 : rb_data <= word04;
5 : rb_data <= word05;
6 : rb_data <= word06;
7 : rb_data <= word07;
8 : rb_data <= word08;
9 : rb_data <= word09;
10: rb_data <= word10;
11: rb_data <= word11;
12: rb_data <= word12;
13: rb_data <= word13;
14: rb_data <= word14;
15: rb_data <= word15;
endcase // case(addr_reg[3:0])
end
//------------------------------------------------------------------
//-- Output state machine:
//-- Read a command fifo entry, act on it, produce ack packet.
//------------------------------------------------------------------
localparam WRITE_PROT_HDR = 0;
localparam WRITE_VRT_HDR = 1;
localparam WRITE_VRT_SID = 2;
localparam WRITE_RB_HDR = 3;
localparam WRITE_RB_DATA = 4;
//the state for the start of packet condition
localparam WRITE_PKT_HDR = (PROT_HDR)? WRITE_PROT_HDR : WRITE_VRT_HDR;
reg [2:0] out_state;
assign out_valid = ~result_fifo_empty;
assign result_fifo_read = out_data[33] && writing;
always @(posedge clock) begin
if (reset) begin
out_state <= WRITE_PKT_HDR;
end
else if (writing && out_data[33]) begin
out_state <= WRITE_PKT_HDR;
end
else if (writing) begin
out_state <= out_state + 1;
end
end
//------------------------------------------------------------------
//-- assign to output fifo interface
//------------------------------------------------------------------
wire [31:0] prot_hdr;
assign prot_hdr[15:0] = 16; //bytes in proceeding vita packet
assign prot_hdr[16] = 1; //yes frame
assign prot_hdr[18:17] = PROT_DEST;
assign prot_hdr[31:19] = 0; //nothing
reg [31:0] out_data_int;
always @* begin
case (out_state)
WRITE_PROT_HDR: out_data_int <= prot_hdr;
WRITE_VRT_HDR: out_data_int <= {12'b010100000000, out_result_hdr[19:16], 2'b0, prot_hdr[15:2]};
WRITE_VRT_SID: out_data_int <= ACK_SID;
WRITE_RB_HDR: out_data_int <= out_result_hdr;
WRITE_RB_DATA: out_data_int <= out_result_data;
default: out_data_int <= 0;
endcase //state
end
assign out_data[35:34] = 2'b0;
assign out_data[33] = (out_state == WRITE_RB_DATA);
assign out_data[32] = (out_state == WRITE_PKT_HDR);
assign out_data[31:0] = out_data_int;
//------------------------------------------------------------------
//-- debug outputs
//------------------------------------------------------------------
assign debug = {
in_state, out_state, //8
in_valid, in_ready, in_data[33:32], //4
out_valid, out_ready, out_data[33:32], //4
command_fifo_empty, command_fifo_full, //2
command_fifo_read, command_fifo_write, //2
addr, //8
strobe_reg, strobe, poke, out_command_has_time //4
};
endmodule //settings_fifo_ctrl

View File

@@ -0,0 +1,116 @@
//
// Copyright 2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Simple I2C core
// Settings reg map:
//
// BASE+0 control register
// byte0 - control bits, data byte, or command bits, prescaler
// byte1 - what to do? (documented in cpp file)
// write prescaler lo
// write prescaler hi
// write control
// write data
// write command
// read data
// read status
//
// Readback:
//
// byte0 has readback value based on the last read command
//
module simple_i2c_core
#(
//settings register base address
parameter BASE = 0,
//i2c line level at reset
parameter ARST_LVL = 1
)
(
//clock and synchronous reset
input clock, input reset,
//32-bit settings bus inputs
input set_stb, input [7:0] set_addr, input [31:0] set_data,
//32-bit data readback
output reg [31:0] readback,
//read is high when i2c core can begin another transaction
output reg ready,
// I2C signals
// i2c clock line
input scl_pad_i, // SCL-line input
output scl_pad_o, // SCL-line output (always 1'b0)
output scl_padoen_o, // SCL-line output enable (active low)
// i2c data line
input sda_pad_i, // SDA-line input
output sda_pad_o, // SDA-line output (always 1'b0)
output sda_padoen_o, // SDA-line output enable (active low)
//optional debug output
output [31:0] debug
);
//declare command settings register
wire [7:0] sr_what, sr_data;
wire sr_changed;
setting_reg #(.my_addr(BASE+0),.width(16)) i2c_cmd_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out({sr_what, sr_data}),.changed(sr_changed));
//declare wb interface signals
wire [2:0] wb_addr;
wire [7:0] wb_data_mosi;
wire [7:0] wb_data_miso;
wire wb_we, wb_stb, wb_cyc;
wire wb_ack;
//create wishbone-based i2c core
i2c_master_top #(.ARST_LVL(ARST_LVL)) i2c
(.wb_clk_i(clock),.wb_rst_i(reset),.arst_i(1'b0),
.wb_adr_i(wb_addr),.wb_dat_i(wb_data_mosi),.wb_dat_o(wb_data_miso),
.wb_we_i(wb_we),.wb_stb_i(wb_stb),.wb_cyc_i(wb_cyc),
.wb_ack_o(wb_ack),.wb_inta_o(),
.scl_pad_i(scl_pad_i),.scl_pad_o(scl_pad_o),.scl_padoen_o(scl_padoen_o),
.sda_pad_i(sda_pad_i),.sda_pad_o(sda_pad_o),.sda_padoen_o(sda_padoen_o) );
//not ready between setting register and wishbone ack
always @(posedge clock) begin
if (reset || wb_ack) ready <= 1;
else if (sr_changed) ready <= 0;
end
//register wishbone data on every ack
always @(posedge clock) begin
if (wb_ack) readback <= {24'b0, wb_data_miso};
end
//assign wishbone signals
assign wb_addr = sr_what[2:0];
assign wb_stb = sr_changed;
assign wb_we = wb_stb && sr_what[3];
assign wb_cyc = wb_stb;
assign wb_data_mosi = sr_data;
endmodule //simple_i2c_core

View File

@@ -0,0 +1,214 @@
//
// Copyright 2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Simple SPI core, the simplest, yet complete spi core I can think of
// Settings register controlled.
// 2 settings regs, control and data
// 1 32-bit readback and status signal
// Settings reg map:
//
// BASE+0 divider setting
// bits [15:0] spi clock divider
//
// BASE+1 configuration input
// bits [23:0] slave select, bit0 = slave0 enabled
// bits [29:24] num bits (1 through 32)
// bit [30] data input edge = in data bit latched on rising edge of clock
// bit [31] data output edge = out data bit latched on rising edge of clock
//
// BASE+2 input data
// Writing this register begins a spi transaction.
// Bits are latched out from bit 0.
// Therefore, load this register in reverse.
//
// Readback
// Bits are latched into bit 0.
// Therefore, data will be in-order.
module simple_spi_core
#(
//settings register base address
parameter BASE = 0,
//width of serial enables (up to 24 is possible)
parameter WIDTH = 8,
//idle state of the spi clock
parameter CLK_IDLE = 0,
//idle state of the serial enables
parameter SEN_IDLE = 24'hffffff
)
(
//clock and synchronous reset
input clock, input reset,
//32-bit settings bus inputs
input set_stb, input [7:0] set_addr, input [31:0] set_data,
//32-bit data readback
output [31:0] readback,
//read is high when spi core can begin another transaction
output ready,
//spi interface, slave selects, clock, data in, data out
output [WIDTH-1:0] sen,
output sclk,
output mosi,
input miso,
//optional debug output
output [31:0] debug
);
wire [15:0] sclk_divider;
setting_reg #(.my_addr(BASE+0),.width(16)) divider_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(sclk_divider),.changed());
wire [23:0] slave_select;
wire [5:0] num_bits;
wire datain_edge, dataout_edge;
setting_reg #(.my_addr(BASE+1),.width(32)) config_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out({dataout_edge, datain_edge, num_bits, slave_select}),.changed());
wire [31:0] mosi_data;
wire trigger_spi;
setting_reg #(.my_addr(BASE+2),.width(32)) data_sr(
.clk(clock),.rst(reset),.strobe(set_stb),.addr(set_addr),.in(set_data),
.out(mosi_data),.changed(trigger_spi));
localparam WAIT_TRIG = 0;
localparam PRE_IDLE = 1;
localparam CLK_REG = 2;
localparam CLK_INV = 3;
localparam POST_IDLE = 4;
localparam IDLE_SEN = 5;
reg [2:0] state;
reg ready_reg;
assign ready = ready_reg && ~trigger_spi;
//serial clock either idles or is in one of two clock states
reg sclk_reg;
assign sclk = sclk_reg;
//serial enables either idle or enabled based on state
wire sen_is_idle = (state == WAIT_TRIG) || (state == IDLE_SEN);
wire [23:0] sen24 = (sen_is_idle)? SEN_IDLE : (SEN_IDLE ^ slave_select);
reg [WIDTH-1:0] sen_reg;
always @(posedge clock) sen_reg <= sen24[WIDTH-1:0];
assign sen = sen_reg;
//data output shift register
reg [31:0] dataout_reg;
wire [31:0] dataout_next = {dataout_reg[30:0], 1'b0};
assign mosi = dataout_reg[31];
//data input shift register
reg [31:0] datain_reg;
wire [31:0] datain_next = {datain_reg[30:0], miso};
assign readback = datain_reg;
//counter for spi clock
reg [15:0] sclk_counter;
wire sclk_counter_done = (sclk_counter == sclk_divider);
wire [15:0] sclk_counter_next = (sclk_counter_done)? 0 : sclk_counter + 1;
//counter for latching bits miso/mosi
reg [6:0] bit_counter;
wire [6:0] bit_counter_next = bit_counter + 1;
wire bit_counter_done = (bit_counter_next == num_bits);
always @(posedge clock) begin
if (reset) begin
state <= WAIT_TRIG;
sclk_reg <= CLK_IDLE;
ready_reg <= 0;
end
else begin
case (state)
WAIT_TRIG: begin
if (trigger_spi) state <= PRE_IDLE;
ready_reg <= ~trigger_spi;
dataout_reg <= mosi_data;
sclk_counter <= 0;
bit_counter <= 0;
sclk_reg <= CLK_IDLE;
end
PRE_IDLE: begin
if (sclk_counter_done) state <= CLK_REG;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
CLK_REG: begin
if (sclk_counter_done) begin
state <= CLK_INV;
if (datain_edge != CLK_IDLE) datain_reg <= datain_next;
if (dataout_edge != CLK_IDLE && bit_counter != 0) dataout_reg <= dataout_next;
sclk_reg <= ~CLK_IDLE; //transition to rising when CLK_IDLE == 0
end
sclk_counter <= sclk_counter_next;
end
CLK_INV: begin
if (sclk_counter_done) begin
state <= (bit_counter_done)? POST_IDLE : CLK_REG;
bit_counter <= bit_counter_next;
if (datain_edge == CLK_IDLE) datain_reg <= datain_next;
if (dataout_edge == CLK_IDLE && ~bit_counter_done) dataout_reg <= dataout_next;
sclk_reg <= CLK_IDLE; //transition to falling when CLK_IDLE == 0
end
sclk_counter <= sclk_counter_next;
end
POST_IDLE: begin
if (sclk_counter_done) state <= IDLE_SEN;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
IDLE_SEN: begin
if (sclk_counter_done) state <= WAIT_TRIG;
sclk_counter <= sclk_counter_next;
sclk_reg <= CLK_IDLE;
end
default: state <= WAIT_TRIG;
endcase //state
end
end
assign debug = {
trigger_spi, state, //4
sclk, mosi, miso, ready, //4
sen[7:0], //8
1'b0, bit_counter[6:0], //8
sclk_counter_done, bit_counter_done, //2
sclk_counter[5:0] //6
};
endmodule //simple_spi_core

View File

@@ -0,0 +1,63 @@
//
// Copyright 2012 Ettus Research LLC
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// User settings bus
//
// Provides 8-bit address, 32-bit data write only bus for user settings, consumes to addresses in
// normal settings bus.
//
// Write user address to BASE
// Write user data to BASE+1
//
// The user_set_stb will strobe after data write, must write new address even if same as previous one.
module user_settings
#(parameter BASE=0)
(input clk,
input rst,
input set_stb,
input [7:0] set_addr,
input [31:0] set_data,
output set_stb_user,
output [7:0] set_addr_user,
output [31:0] set_data_user
);
wire addr_changed, data_changed;
reg stb_int;
setting_reg #(.my_addr(BASE+0),.width(8)) sr_0
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(set_addr_user),.changed(addr_changed) );
setting_reg #(.my_addr(BASE+1)) sr_1
(.clk(clk),.rst(rst),.strobe(set_stb),.addr(set_addr),
.in(set_data),.out(set_data_user),.changed(data_changed) );
always @(posedge clk)
if (rst|set_stb_user)
stb_int <= 0;
else
if (addr_changed)
stb_int <= 1;
assign set_stb_user = stb_int & data_changed;
endmodule // user_settings