diff options
Diffstat (limited to 'fpga/usrp2/control_lib')
| -rw-r--r-- | fpga/usrp2/control_lib/Makefile.srcs | 2 | ||||
| -rw-r--r-- | fpga/usrp2/control_lib/settings_bus_crossclock.v | 9 | ||||
| -rw-r--r-- | fpga/usrp2/control_lib/settings_fifo_ctrl.v | 392 | ||||
| -rw-r--r-- | fpga/usrp2/control_lib/simple_spi_core.v | 214 | 
4 files changed, 613 insertions, 4 deletions
| diff --git a/fpga/usrp2/control_lib/Makefile.srcs b/fpga/usrp2/control_lib/Makefile.srcs index 6ee7ea262..0bb9a3efe 100644 --- a/fpga/usrp2/control_lib/Makefile.srcs +++ b/fpga/usrp2/control_lib/Makefile.srcs @@ -55,4 +55,6 @@ atr_controller16.v \  fifo_to_wb.v \  gpio_atr.v \  user_settings.v \ +settings_fifo_ctrl.v \ +simple_spi_core.v \  )) diff --git a/fpga/usrp2/control_lib/settings_bus_crossclock.v b/fpga/usrp2/control_lib/settings_bus_crossclock.v index 9c5912042..a61ee8fad 100644 --- a/fpga/usrp2/control_lib/settings_bus_crossclock.v +++ b/fpga/usrp2/control_lib/settings_bus_crossclock.v @@ -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 diff --git a/fpga/usrp2/control_lib/settings_fifo_ctrl.v b/fpga/usrp2/control_lib/settings_fifo_ctrl.v new file mode 100644 index 000000000..82651e776 --- /dev/null +++ b/fpga/usrp2/control_lib/settings_fifo_ctrl.v @@ -0,0 +1,392 @@ +// +// 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 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; + +    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 <= READ_LINE0; +        end +        else begin +            case (in_state) + +            READ_LINE0: begin +                if (reading/* && in_data[32]*/) 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 <= READ_LINE0; +            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 diff --git a/fpga/usrp2/control_lib/simple_spi_core.v b/fpga/usrp2/control_lib/simple_spi_core.v new file mode 100644 index 000000000..3c0ed60b9 --- /dev/null +++ b/fpga/usrp2/control_lib/simple_spi_core.v @@ -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 | 
