From: Luke Kenneth Casson Leighton Date: Sun, 22 Jul 2018 06:49:39 +0000 (+0100) Subject: add first peripheral set X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=821a48963171cf5a7cb3848a8d5203f933d16757;p=shakti-peripherals.git add first peripheral set --- diff --git a/src/lib/ClockDiv.bsv b/src/lib/ClockDiv.bsv new file mode 100644 index 0000000..b84894d --- /dev/null +++ b/src/lib/ClockDiv.bsv @@ -0,0 +1,44 @@ +package ClockDiv; + /*=== Project imports ==*/ + import Clocks::*; + /*======================*/ + // =========================== Clock divider module ================ // + interface Ifc_ClockDiv#(numeric type width); + interface Clock slowclock; + method Action divisor(Bit#(width) in); + endinterface + + module mkClockDiv(Ifc_ClockDiv#(width)); + let defclock <- exposeCurrentClock; + Reg#(Bit#(1)) clk <- mkReg(0); + Reg#(Bit#(width)) rg_divisor <- mkReg(0); + Reg#(Bit#(width)) rg_counter <- mkReg(0); + MakeClockIfc#(Bit#(1)) new_clock <- mkUngatedClock(0); + MuxClkIfc clock_selector <- mkUngatedClockMux(new_clock.new_clk,defclock); + Bool clockmux_sel = rg_divisor!=0; + rule increment_counter; + if(rg_divisor!=0 && rg_counter >= rg_divisor)begin + rg_counter <= 0; + clk <= ~ clk; + end + else + rg_counter <= rg_counter + 1; + endrule + + rule generate_clock; + new_clock.setClockValue(clk); + endrule + + rule select_clock; + clock_selector.select(clockmux_sel); + endrule + + method Action divisor(Bit#(width) in); + rg_divisor <= in != 0 ? in - 1 : 0; + endmethod + + interface slowclock=clock_selector.clock_out; + endmodule + // ============================================================== // + +endpackage diff --git a/src/lib/ConcatReg.bsv b/src/lib/ConcatReg.bsv new file mode 100644 index 0000000..3020e05 --- /dev/null +++ b/src/lib/ConcatReg.bsv @@ -0,0 +1,419 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ + +// Copyright (c) 2016 Massachusetts Institute of Technology + +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation +// files (the "Software"), to deal in the Software without +// restriction, including without limitation the rights to use, copy, +// modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS +// BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +// This file was created by gen_ConcatReg.py. If you want to modify this file, +// please modify gen_ConcatReg.py instead. If you need a wider concatReg +// function, change the value of n in gen_ConcatReg.py and run it again. + +// The Bluespec provided BuildVector.bsv provides another example of +// constructing a function that takes a variable number of arguments + +// Typeclass for creating _concatReg with a variable number of arguments. +typeclass ConcatReg#(type r, numeric type n1, numeric type n2) + dependencies ((r,n1) determines n2, (r,n2) determines n1); + // dependencies (r determines (n1,n2)); + function r _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2); +endtypeclass +// Base case +instance ConcatReg#(Reg#(Bit#(n3)), n1, n2) provisos (Add#(n1, n2, n3)); + function Reg#(Bit#(TAdd#(n1,n2))) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2); + return (interface Reg; + method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read}; + method Action _write(Bit#(TAdd#(n1,n2)) x); + r1._write(truncateLSB(x)); + r2._write(truncate(x)); + endmethod + endinterface); + endfunction +endinstance +// Recursion +instance ConcatReg#(function r f(Reg#(Bit#(n3)) r3), n1, n2) provisos (ConcatReg#(r, TAdd#(n1, n2), n3)); + function function r f(Reg#(Bit#(n3)) r3) _concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2); + return _concatReg(interface Reg; + method Bit#(TAdd#(n1,n2)) _read = {r1._read, r2._read}; + method Action _write(Bit#(TAdd#(n1,n2)) x); + r1._write(truncateLSB(x)); + r2._write(truncate(x)); + endmethod + endinterface); + endfunction +endinstance + +function Reg#(t) readOnlyReg(t r); + return (interface Reg; + method t _read = r; + method Action _write(t x) = noAction; + endinterface); +endfunction + +// Wrapper function for users. This can take a variable number of arguments. +// You will need to use asReg() for the third argument and beyond. +function r concatReg(Reg#(Bit#(n1)) r1, Reg#(Bit#(n2)) r2) provisos(ConcatReg#(r, n1, n2)); + return _concatReg(asReg(r1),asReg(r2)); +endfunction + +// Automatically generated macros with a set number of registers. +// These don't require asReg when used. +function Reg#(Bit#(n)) concatReg2( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2 + ) provisos ( + Add#(n1,n2,n) + ); + return concatReg(asReg(r1),asReg(r2)); +endfunction + +function Reg#(Bit#(n)) concatReg3( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3 + ) provisos ( + Add#(TAdd#(n1,n2),n3,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3)); +endfunction + +function Reg#(Bit#(n)) concatReg4( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4 + ) provisos ( + Add#(TAdd#(TAdd#(n1,n2),n3),n4,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4)); +endfunction + +function Reg#(Bit#(n)) concatReg5( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5)); +endfunction + +function Reg#(Bit#(n)) concatReg6( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6)); +endfunction + +function Reg#(Bit#(n)) concatReg7( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7)); +endfunction + +function Reg#(Bit#(n)) concatReg8( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8)); +endfunction + +function Reg#(Bit#(n)) concatReg9( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9)); +endfunction + +function Reg#(Bit#(n)) concatReg10( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10)); +endfunction + +function Reg#(Bit#(n)) concatReg11( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11)); +endfunction + +function Reg#(Bit#(n)) concatReg12( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12)); +endfunction + +function Reg#(Bit#(n)) concatReg13( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12, + Reg#(Bit#(n13)) r13 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13)); +endfunction + +function Reg#(Bit#(n)) concatReg14( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12, + Reg#(Bit#(n13)) r13, + Reg#(Bit#(n14)) r14 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14,n) + ); + return concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14)); +endfunction + +function Reg#(Bit#(n)) concatReg24( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12, + Reg#(Bit#(n13)) r13, + Reg#(Bit#(n14)) r14, + Reg#(Bit#(n15)) r15, + Reg#(Bit#(n16)) r16, + Reg#(Bit#(n17)) r17, + Reg#(Bit#(n18)) r18, + Reg#(Bit#(n19)) r19, + Reg#(Bit#(n20)) r20, + Reg#(Bit#(n21)) r21, + Reg#(Bit#(n22)) r22, + Reg#(Bit#(n23)) r23, + Reg#(Bit#(n24)) r24 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20),n21),n22),n23),n24,n) + ); + return + concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20),asReg(r21),asReg(r22),asReg(r23),asReg(r24)); +endfunction + +function Reg#(Bit#(n)) concatReg20( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12, + Reg#(Bit#(n13)) r13, + Reg#(Bit#(n14)) r14, + Reg#(Bit#(n15)) r15, + Reg#(Bit#(n16)) r16, + Reg#(Bit#(n17)) r17, + Reg#(Bit#(n18)) r18, + Reg#(Bit#(n19)) r19, + Reg#(Bit#(n20)) r20 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19),n20,n) + ); + return + concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19),asReg(r20)); +endfunction + +function Reg#(Bit#(n)) concatReg18( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12, + Reg#(Bit#(n13)) r13, + Reg#(Bit#(n14)) r14, + Reg#(Bit#(n15)) r15, + Reg#(Bit#(n16)) r16, + Reg#(Bit#(n17)) r17, + Reg#(Bit#(n18)) r18 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18,n) + ); + return + concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18)); +endfunction + +function Reg#(Bit#(n)) concatReg16( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12, + Reg#(Bit#(n13)) r13, + Reg#(Bit#(n14)) r14, + Reg#(Bit#(n15)) r15, + Reg#(Bit#(n16)) r16 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16,n) + ); + return + concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16)); +endfunction +function Reg#(Bit#(n)) concatReg19( + Reg#(Bit#(n1)) r1, + Reg#(Bit#(n2)) r2, + Reg#(Bit#(n3)) r3, + Reg#(Bit#(n4)) r4, + Reg#(Bit#(n5)) r5, + Reg#(Bit#(n6)) r6, + Reg#(Bit#(n7)) r7, + Reg#(Bit#(n8)) r8, + Reg#(Bit#(n9)) r9, + Reg#(Bit#(n10)) r10, + Reg#(Bit#(n11)) r11, + Reg#(Bit#(n12)) r12, + Reg#(Bit#(n13)) r13, + Reg#(Bit#(n14)) r14, + Reg#(Bit#(n15)) r15, + Reg#(Bit#(n16)) r16, + Reg#(Bit#(n17)) r17, + Reg#(Bit#(n18)) r18, + Reg#(Bit#(n19)) r19 + ) provisos ( + Add#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(TAdd#(n1,n2),n3),n4),n5),n6),n7),n8),n9),n10),n11),n12),n13),n14),n15),n16),n17),n18),n19,n) + ); + return + concatReg(asReg(r1),asReg(r2),asReg(r3),asReg(r4),asReg(r5),asReg(r6),asReg(r7),asReg(r8),asReg(r9),asReg(r10),asReg(r11),asReg(r12),asReg(r13),asReg(r14),asReg(r15),asReg(r16),asReg(r17),asReg(r18),asReg(r19)); +endfunction diff --git a/src/lib/axi_addr_generator.bsv b/src/lib/axi_addr_generator.bsv new file mode 100644 index 0000000..1c07af5 --- /dev/null +++ b/src/lib/axi_addr_generator.bsv @@ -0,0 +1,40 @@ +package axi_addr_generator; + /*=== Project imports ===*/ + import defined_types::*; + `include "instance_defines.bsv" + /*=======================*/ + + // This function is used by the slaves on the AXI4 bus to generate the sequential addresses in burst mode. + // the different modes supported are : + // FIXED: the same address is sent in all transactions. Typically used in polling modes. + // INCR: The address is simply incremented arlen number of times from the starting address. + // WRAP: This mode supports only 4 valid lengths: 2, 4 8 and 16 bursts. the increments happen in a wrap arouind fashion. + function Bit#(`PADDR) burst_address_generator(Bit#(8) arlen, Bit#(3) arsize, Bit#(2) arburst, Bit#(`PADDR) address); + + // this variable will decide the index above which part of the address should + // not change in WRAP mode. Bits below this index value be incremented according + // to the value of arlen and arsize; + Bit#(3) wrap_size; + case(arlen) + 3: wrap_size= 2; + 7: wrap_size= 3; + 15: wrap_size=4; + default:wrap_size=1; + endcase + + Bit#(`PADDR) new_address=address+(('b1)< 0); + cprescaler <= cprescaler - 1; + endrule + + (* doc = "A tick is sent which will fire the rules making it emulate a module clock, when counter becomes zero" *) + rule restore_prescale(cprescaler == 0 && rprescaler > 0); + cprescaler <= rprescaler; + pwI2C.send; + // $display("tick i2c sent"); + // $display("cprescaler : %d rprescaler : %d",cprescaler,rprescaler,$time); + endrule + + (* doc = "This rule is used to count down SCL clock which is a scaled down version of the Module clock derived from the system clock. SCL toggles only during transmission. For Multiple masters, different scenarios might arise" *) + rule count_scl(coSCL > 0 && pwesoCond && st_toggle); //~ st_toggle dec false + coSCL <= coSCL - 1; + // $display("coSCL: %d", coSCL - 1); + endrule + + (* doc = "This rule is used to restore the SCL count value. Sending a tick, operating as scl clock" *) + rule restore_scl(coSCL == 0 && pwI2C && reSCL > 0); + coSCL <= reSCL; + pwSCL.send; + // $display("tick sent"); + endrule + + + //Master Transmitter Mode - Transmission + //Test sending through SDA + //When integrated with AXI, this rule should fire whenever R/W bit of the slave address is 0 + /*rule check_bus_busy(mTransFSM == Idle && pwI2C && eso==1'b1); + if(statusReg[0] != 1) + mTransFSM <= ContCheck; //If Bus is busy, the control does not move forward -- For Multiple Masters only + endrule*/ + + (* doc = "This rule is used to toggle the Serial Clock line based on coSCL input" *) + rule toggle_scl(st_toggle && pwSCL); //~ st_toggle is false + val_SCL <= ~val_SCL; + // $display("From toggle rule :",~val_SCL); + //$display("TriState SCL Value : %b", line_SCL._read,$time); + endrule + + + +//////////////State Machine Implementation //////////////////////////////// +/* + Initially, Module Is Off Eso = 0. + Only Check Config Register And write into register rules should fire. + +ESO = 0 => Go to reset state. + +Interrupt Generation And Documentation + Interrupt Vector = S3 Default Value: 00000000 + 01.) 00H = Clock register Yet Not Initialized + 02.) 01H = Address has been sent waiting for slave to acknowledge // Not generated + 03.) 02H = Slave has acknowledged waitng for Data to be written + 04.) 03H = Slave Sent A Nack in send address phase + 05.) 04H = Slave Sent A Nack in Write Data Phase (data was being written to slave) + 06.) 05H = Master Was unable to send a ack during receive data phase + 07.) 06H = Master Received A data from slave please read + 08.) 07H = I2C Reset By Reset Signal //Not generated + 09.) 08H = Master Received Last Byte From Slave + 10.) 09H = Idle i.e Not Busy // Have to modify in multi master .i2c might be idle yet busy + 11.) 0AH = RT Strart sent // If int then it means enter address + 12.) 0BH = Start Sent + 13.) 0CH = timeout + 14.) +////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +*/ +/////////////////////////////////////////////////////////////////////////// + + rule idler(mTransFSM == Idle); + if(cycwaste == 0) begin + /* dOutEn <= True; + cOutEn <= True; + val_SDA <= 1; + val_SCL <= 1;*/ + cycwaste <= cycwaste + 1; + end + else if(cycwaste == 'd600) + begin + mTransFSM <= Idleready; + s3 <= 'h09; + cycwaste <= 0; + sendInd <= 2; + i2ctimeout <= 1; + // Should it leave control of SCL And SDA Line + + statusReg <= 1; + resetcount <= 0 ; + end + else + cycwaste <= cycwaste + 1; + endrule + (* mutually_exclusive = "check_control_reg, send_start_trans" *) + (* doc = "Checks the control Reg (after Driver updates it) and based on byte loaded goes to Write/Read" *) + rule check_control_reg(configchange == 1 && pwesoCond && !intCond && mTransFSM != Idle && mTransFSM !=NAck); //~ mod edge and serial on + $display("Configchanged fire"); + configchange <= 0 ; // Discuss For More than 1 enques + if(controlReg[2:1] == 2 && bb==0) begin // Start + $display("Invalid Start"); + end + else if(controlReg[2:1] == 2) + begin + mTransFSM <= STA; + st_toggle <= True; + val_SDA <= 1; + $display("Start Received"); + dOutEn <= True; + cOutEn <= True; + + end + else if(controlReg[2:1] == 1 ) begin // Stop + $display("Invalid Stop",mTransFSM); + mTransFSM <= End; + dOutEn <= True; + cOutEn <= True; + sendInd <= 2; + ber <=1; //~ add bus error functionality + end + endrule + + (* doc = "Reset the I2C State Machine and wait for another transaction request" *) + rule reset_state(mTransFSM == ResetI2C && pwI2C && ber == 0); + st_toggle <= False; + dOutEn <= False; + cOutEn <= False; + i2ctimeout <= 1; + // Should it leave control of SCL And SDA Line + controlReg <= 128; + statusReg <= 1; + resetcount <= 0 ; + sendInd <= 2; + mTransFSM <= Idle; + + //~/ should we have a reset interupt + endrule + + + + + + (* doc = "Send Start bit to the Slave" *) + rule send_start_trans(val_SCL_in == 1 && startBit && pwesoCond && bb == 1); //~ pwi2c & i2c on & both transrec - sta + + $display("Came Here",sendInd); + if(val_SDA == 0) + begin + mTransFSM <= SendAddr; //~Once Cycle Delay whats 0-1 ? + $display("start sent"); + bb <= 0; + sta <=0 ; + s3 <= 'h0B; + end + else + begin + val_SDA <= startSig[sendInd]; + sendInd <= sendInd-1; + end //TODO check what happens when multiple start has to be send!!!! + endrule + + + + + rule send_rtstart_trans(val_SCL_in == 1 && mTransFSM == RTSTA && pwesoCond ); //~ pwi2c & i2c on & both transrec - sta + $display("Here"); + if(val_SDA == 0) //If I read val_SDA - Next transaction is x or it is normal--- Why? //~ it was because of code bug send ind going to -1 + begin + mTransFSM <= Intrpt; //~Once Cycle Delay whats 0-1 ? + $display("RT start sent"); + pin<=0; + i2ctimeout <=1; + bb <= 0; + sta <=0 ; + s3 <= 'h0A; + end + else + begin + val_SDA <= startSig[sendInd]; + sendInd <= sendInd-1; + end //TODO check what happens when multiple start has to be send!!!! + endrule + + (* doc = "Send Slave Address for Transaction" *) + rule send_addr(sendAddr && sclSync); // Fires On Falling Edge + sendInd <= 2; + if(dataBit == 0) begin + dOutEn <= False; + mTransFSM <= Ack; + s3 <= 8'b1; + if(s0[0] == 0) + operation <= Write; + else + operation <= Read; + $display("Address Sent"); + end + else + begin + dataBit <= dataBit - 1; + val_SDA <= s0[dataBit-1]; + $display("Sending Bit %d In neg cycle Bit %d ", dataBit - 1,s0[dataBit-1]); + end + + endrule + + + + + + + rule check_Ack(ackCond && pwsclCond); //Should Fire When SCL is high //~shouldn't it be pwsclcond or sclsync + //$display("Value : %d ,Condition : ",line_SDA,line_SDA!=0 ); + dataBit <= 8; + i2ctimeout <= 1; + if(val_SDA_in != 0 && val_SCL == 0 ) //Line SCL is actually high + begin //Bus Error + ad0_lrb <= 1; + mTransFSM <= NAck; + dOutEn <= True; + ber <= 1; + if(sendAddr) + s3 <= 'h03; + else if(mTransFSM == ReadData) + s3 <= 'h04; + else + s3 <= 'h05; + $display("Acknowledgement Not Received Bus Error"); + end + else if(val_SCL == 1) begin // Acknowledgement is done // Here line scl is actually low + pin <= 0; + $display("eni : %d",eni); + if(mTransFSM == SendAddr || mTransFSM == SendData ) + s3 <= 'h02; + else + s3 <= 'h06; + + ad0_lrb <= 0; + dOutEn<= True; + mTransFSM <= Intrpt; + $display("Acknowledgement Received. Waiting For Interupt Serve ",$time); + // $finish(); + end + endrule + + + + + + + + (* doc = "Wait for the generated interrupt to be serviced" *) + rule wait_interrupt(intCond && pwesoCond && val_SCL_in ==0 ); + if(pin == 1'b0) begin + + // $display("Waiting For Interrupt %h",controlReg,$time); //Pitfall if eso was turned off in wait interupt + val_SCL <= 0; + if(i2ctime[14] == 1) + i2ctimeout <= i2ctimeout + 1; //~ Have to add timer interupt also reset timer count + + if(i2ctimeout <= i2ctime[13:0] ) //14 as enable int 15 as int + begin //Reset state + mTransFSM <= End; i2ctime[15] <=1; + s3 <= 'h0C; + end + else + st_toggle <= False; + end + else begin + + $display("Interupt Is Served. Along with pin & control reg - %d & operation - %d ",controlReg,operation); + i2ctimeout <= 1; + st_toggle <= True; + if(controlReg[2:1] == 2) + begin + $display("Invalid Start"); + ber <=1 ; + mTransFSM <= ResetI2C; + end + else if(controlReg[2:1] == 1) + begin //Signalling the end of transaction + mTransFSM <= End; + val_SDA <= 0; + $display("Received Stop Condition ",val_SDA); + end + else if(s3 == 'h0A) begin + mTransFSM <= SendAddr; + dOutEn <= True; + $display("Sending RT Address Bit %d In negative cycle Bit %d",dataBit - 1 , s0[dataBit - 1]); + dataBit <= dataBit - 1; + val_SDA <= s0[dataBit - 1]; + + end + else if(operation == Write) begin + mTransFSM <= SendData; + dOutEn <= True; + $display("Sending Bit %d In negative cycle Bit %d",dataBit - 1 , s0[dataBit - 1]); + dataBit <= dataBit - 1; + val_SDA <= s0[dataBit - 1]; + end + + else begin + if(ack == 0) //~ Value SCL what hc0 doesnt = nack? + last_byte_read <= True; + if(val_SCL == 0) begin //Hopefully this should ward off read sync error that might //~ discuss maybe + mTransFSM <= ReadData; + dOutEn <= False; //~ when we set true + end + end + end + endrule + + (* doc = "Shift the 8-bit data through the SDA line at each low pulse of SCL" *) + rule send_data(mTransFSM == SendData && sclSync); + $display("WData: TriState SDA Value : %b", val_SDA._read,$time); + if(dataBit == 'd0) begin //~ smthhng + mTransFSM <= Ack; + dOutEn <= False; + $display("Leaving Bus For Acknowledge"); + end + else + begin + $display("Sending Bit %d In negative cycle Bit %d",dataBit - 1 , s0[dataBit - 1]); + dataBit <= dataBit - 1; + val_SDA <= s0[dataBit - 1]; + end + endrule + + /* + (* doc = "Send a NoAck to Slave signifying the end of read transaction" *) + rule send_Nack(mTransFSM == NAck && pwsclCond); //~ + val_SDA <= 1; + if(line_SCL._read == 0) begin //~ why scl + mTransFSM <= End; + end + //Makes sense after interrupt support is added + endrule + */ + + (* doc = "Receive the 8-bit data through the SDA line at each high pulse of SCL" *) + rule receive_data(mTransFSM == ReadData && sclnSync); //~ Rising Edge + $display("Receiving Bit %d In Positive Cycle And Bit %d",dataBit - 1,val_SDA_in); + dataBit <= dataBit - 1; + s0[dataBit - 1 ] <= val_SDA_in; + endrule + + rule resetfilter; + if(rstsig == 1) + begin + resetcount <= resetcount + 1; + if(resetcount == 'd59) + begin + ber <= 0; + mTransFSM <= ResetI2C; + $display("Resetting"); + s3 <= 'h07; + end + end + else + resetcount <= 0 ; + endrule + + rule receive_data1((mTransFSM == ReadData || mTransFSM == NAck_1) && sclSync && ( dataBit == 0 || dataBit == 8) ); //~ Falling Edge + if(dataBit == 0) + begin + if(!last_byte_read) begin + $display("Going to Ack, Taking controll of the bus btw"); + mTransFSM <= Ack; + val_SDA <= 0; + dOutEn <= True; + end + else begin + $display("Going to NAck, Taking controll of the bus btw"); + mTransFSM <= NAck_1; + val_SDA <= 1; + dataBit <= 8 ; + dOutEn <= True; + pin <=0 ; + s3 <= 'h08; + last_byte_read <= False; + end + end + else + mTransFSM <= NAck; + endrule + + rule wait_interrupt_receive_end ( dataBit == 8 && mTransFSM == NAck && val_SCL_in == 0); + if(controlReg[2:1] == 1) + begin + mTransFSM <= End; + val_SDA <= 0; + st_toggle <= True; + end + else + st_toggle<=False; //Stop Signal goes from 0 to 1 + endrule + + + (* doc = "Send a STOP bit signifying no more transaction from this master" *) + rule send_stop_condition(stopBit && pwesoCond && val_SCL_in == 1); //~ it might be fal edge + $display("Sending Stop SDA Value : %b SCL Value : %b", val_SDA._read,val_SCL._read,$time); + + if(val_SDA == 1) begin + mTransFSM <= Idle; + // statusReg <= 'b0000001; + sto <= 0; + st_toggle <= False; + //Interrupt needs to be sent and final course of action is determined + end + else + begin + val_SDA <= stopSig[sendInd]; + sendInd <= sendInd - 1; end + endrule + + //Rules for Communicating with AXI-4 + //TODO - Code different conditions such as only certain registers can be accessed at certain times + rule rl_rd_req_rsp; + //TODO - What if a read request is issued to the data register + $display("AXI Read Request time %d pin %d",$time,pin); + let req <- pop_o (s_xactor.o_rd_addr); + I2C i2c = S0; + if(truncate(req.araddr) == pack(i2c)) begin + pin <=1; + $display("Setting pin to 1 in read phase"); + end + else $display("Not equal %h, %h",req.araddr,pack(i2c)); + I2C i2c1 = Status; + if(truncate(req.araddr) == pack(i2c1)) begin + $display("Clearing Status Bits"); + ber <= 0; + end + + I2C i2c2 = SCL; + I2C i2c3 = Time; + Bit#(`Reg_width) rdreg ; + if(truncate(req.araddr) == pack(i2c2)) + rdreg = duplicate(c_scl); + else if(truncate(req.araddr) == pack(i2c3)) + rdreg = duplicate(i2ctime); + else + rdreg = duplicate(get_i2c(unpack(truncate(req.araddr)))); + $display("Register Read %h: Value: %d ",req.araddr,rdreg); + let resq = AXI4_Lite_Rd_Data {rresp : AXI4_LITE_OKAY, rdata : rdreg ,ruser: 0}; + s_xactor.i_rd_data.enq(resq); + + endrule + + rule rl_wr_req; + $display("AXI Write Request ",$time); + let wr_addr <- pop_o (s_xactor.o_wr_addr); + let wr_data <- pop_o (s_xactor.o_wr_data); + $display("Wr_addr : %h Wr_data: %h", wr_addr.awaddr, wr_data.wdata); + set_i2c(unpack(truncate(wr_addr.awaddr)),truncate(wr_data.wdata)); + let resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_SLVERR, buser: wr_addr.awuser}; + if(ber==1) + resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_SLVERR, buser: wr_addr.awuser}; + else + resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_OKAY, buser: wr_addr.awuser}; + s_xactor.i_wr_resp.enq(resp); + $display("Received Value %d",wr_data.wdata); + endrule + + + + //Interface Definitions + /* interface I2C_out out; + interface sda = line_SDA.io; + interface scl = line_SCL.io; + endinterface*/ + + interface I2C_out out; + method Bit#(1) scl_out; + return val_SCL; + endmethod + method Bit#(1) i2c_DRV0; + return drv0_rg[0]; + endmethod + method Bit#(1) i2c_DRV1; + return drv1_rg[0]; + endmethod + method Bit#(1) i2c_DRV2; + return drv2_rg[0]; + endmethod + method Bit#(1) i2c_PD; + return pd_rg[0]; + endmethod + method Bit#(1) i2c_PPEN; + return ppen_rg[0]; + endmethod + method Bit#(1) i2c_PRG_SLEW; + return prg_slew_rg[0]; + endmethod + method Bit#(1) i2c_PUQ; + return puq_rg[0]; + endmethod + method Bit#(1) i2c_PWRUPZHL; + return pwrupzhl_rg[0]; + endmethod + method Bit#(1) i2c_PWRUP_PULL_EN; + return pwrup_pull_en_rg[0]; + endmethod + method Action scl_in(Bit#(1) in); + val_SCL_in <= in; + endmethod + method Bool scl_out_en; + return (cOutEn && eso == 1'b1); + endmethod + method Bit#(1) sda_out; + return val_SDA; + endmethod + method Action sda_in(Bit#(1) in); + val_SDA_in <= in; + endmethod + method Bool sda_out_en; + return (dOutEn && eso == 1'b1); + endmethod + endinterface + + //AXI-4 Interface + interface slave_i2c_axi = s_xactor.axi_side; + + method Bit#(1) isint=~pin & eni; + /*let lv_res= + if(lv_res==1) + $display($time,"I2C: Interrupt is high"); + return lv_res;*/ +// endmethod + + method Bit#(1) isber = ber; + + method Bit#(1) timerint(); + return i2ctime[15]; + endmethod + + method Action resetc( Bit#(1) rst ); + rstsig <= rst; + endmethod + //Test Interface + /* method Action set_eso(Bit#(1) temp_eso); + eso <= temp_eso; + endmethod*/ +/* + method Action set_s2(Bit#(8) temp_s2); + s2 <= temp_s2; + endmethod + + method Action set_s1(Bit#(8) temp_s1); + mcontrolReg <= temp_s1; + /*pin <= temp_s1[7]; + eso <= temp_s1[6]; + es1 <= temp_s1[5]; + es2 <= temp_s1[4]; + eni <= temp_s1[3]; + sta <= temp_s1[2]; + sto <= temp_s1[1]; + ack <= temp_s1[0]; + $display("temp_s1: %b",temp_s1); + endmethod + + method Action set_s0(Bit#(8) temp_s0); + s0 <= temp_s0; + endmethod +*/ + endmodule +// +// (*synthesize*) +// module mkI2CController_wrap(I2C_IFC_wrap); +// I2C_IFC dut <- mkI2CController(); +// TriState#(Bit#(1)) line_SCL <- mkTriState(dut.out.scl_out_en, dut.out.scl_out); +// TriState#(Bit#(1)) line_SDA <- mkTriState(dut.out.sda_out_en, dut.out.sda_out); +// rule send_input_scl; +// dut.out.scl_in(line_SCL._read); +// endrule +// rule send_input_sda; +// dut.out.sda_in(line_SDA._read); +// endrule +// interface slave_i2c_axi_wrap = dut.slave_i2c_axi; +// method isint_wrap = dut.isint; +// method resetc_wrap = dut.resetc; +// method timerint_wrap = dut.timerint; +// method isber_wrap = dut.isber; +// interface I2C_out_tri out_tri; +// interface sda = line_SDA.io; +// interface scl = line_SCL.io; +// endinterface +// endmodule +// =============================================== +/* +// =============================================== +// Test Bench + module mkTb(Empty); + Reg#(Bit#(32)) counter <- mkReg(0); + //Reg#(Bit#(1)) scl_dr <- mkReg(0); + //Reg#(Bit#(1)) sda_dr <- mkReg(0); + TriState#(Bit#(1)) scl_dr <- mkTriState(False,0); + TriState#(Bit#(1)) sda_dr <- mkTriState(False,0); + I2C_IFC test <- mkI2CController(); + + rule count; + counter <= counter + 1; + //$display("counter : %d",counter); + if(counter == 50000) + $finish(0); + endrule + + rule send_values(counter == 1); + //test.set_eso(1'b1); + test.set_s2(8'd4); + test.set_s1('hc5); + test.set_s0('ha5); + endrule + + endmodule +// =============================================== +*/endpackage diff --git a/src/peripherals/plic/plic.bsv b/src/peripherals/plic/plic.bsv new file mode 100644 index 0000000..0e94ee7 --- /dev/null +++ b/src/peripherals/plic/plic.bsv @@ -0,0 +1,381 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ +package plic; + import Vector::*; + //import defined_parameters::*; + import defined_types::*; + import ConfigReg::*; + import Semi_FIFOF::*; + import AXI4_Lite_Types::*; + import BUtils ::*; + import ConcatReg ::*; + `include "instance_defines.bsv" + // import ConfigReg::*; +/*Platform level interrupt controller: + Refer to RISC-V privilege spec-v-1.10 chapter 7 + Memory maps of Registers + Interrupt enable registers : + rg_ie_0 :0C002000 + rg_ie_1 :0C002000 + rg_ie_2 :0C002000 + . + . + . + rg_ie_7 :0C002000 + rg_ie_8 :0C002001 + rg_ie_9 :0C002001 + . + . + Interrupt priority registers : + rg_priority_0 : 0C000000 + rg_priority_1 : 0C000002 + rg_priority_2 : 0C000004 + . + . + . + Priority Threshold register : + rg_priority_threshold : 0C200000 + Claim register : + rg_interrupt_id : 0C200004 +*/ + + +interface Ifc_PLIC#(numeric type addr_width,numeric type word_size,numeric type no_of_ir_pins); + interface Vector#(no_of_ir_pins,Ifc_global_interrupt) ifc_external_irq; + interface Ifc_program_registers#(addr_width,word_size) ifc_prog_reg; + method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note; + method ActionValue#(Bit#(TLog#(no_of_ir_pins))) intrpt_completion; +endinterface + +//(*conflict_free = "rl_prioritise, prog_reg"*) +module mkplic(Ifc_PLIC#(addr_width,word_size,no_of_ir_pins)) + provisos( + Log#(no_of_ir_pins, priority_bits), + Mul#(8,word_size,data_width), + Add#(1,priority_bits,x_priority_bits), + Add#(msb_priority,1,priority_bits), + Add#(msb_priority_bits,1,no_of_ir_pins), + Add#(b__, no_of_ir_pins, data_width), + Add#(c__, priority_bits, data_width), + Add#(a__, 8, no_of_ir_pins), + Add#(e__, 32, data_width), + Add#(g__, 32, no_of_ir_pins), + Add#(f__, 3, priority_bits), + Add#(d__, 5, priority_bits) + ); + let v_no_of_ir_pins = valueOf(no_of_ir_pins); + let v_priority_bits = valueOf(priority_bits); + let v_msb_priority_bits = valueOf(msb_priority_bits); + let v_msb_priority = valueOf(msb_priority); + let v_data_width = valueOf(data_width); + + //(* noinline *) + + /* This function defines the working of priority encoder with 4 bit inputs */ + function Bit#(8) priority_encoder(Bit#(8) inp, Bool alu_free); + Bit#(8) outp = 0; + if(alu_free) begin + if(inp[0]==1) + outp[0] = 1'b1; + else if(inp[1]==1) + outp[1] = 1'b1; + else if(inp[2]==1) + outp[2] = 1'b1; + else if(inp[3]==1) + outp[3] = 1'b1; + else if(inp[4]==1) + outp[4] = 1'b1; + else if(inp[5]==1) + outp[5] = 1'b1; + else if(inp[6]==1) + outp[6] = 1'b1; + else if(inp[7]==1) + outp[7] = 1'b1; + end + + return outp; + endfunction + + function bit any_req(Bit#(8) inp); + return inp[0] | inp[1] | inp[2] | inp[3] | inp[4] | inp[5] | inp[6] | inp[7]; + endfunction + + /* Encodes the grant vector */ + function Bit#(x_priority_bits) encoder(Bit#(no_of_ir_pins) inp); + Bit#(priority_bits) outp = 0; + bit outp_valid = 1'b1; + for(Integer i = 0; i < v_no_of_ir_pins; i = i+1) begin + if(inp[i]==1) + outp = fromInteger(i); + end + return {outp_valid,outp}; + endfunction + + function Reg#(t) readOnlyReg(t r); + return (interface Reg; + method t _read = r; + method Action _write(t x) = noAction; + endinterface); + endfunction + + /* Request vectors are passed down the tree and the grants are given back */ + function Bit#(no_of_ir_pins) encoder_tree(Bit#(no_of_ir_pins) inp); + Bit#(no_of_ir_pins) outp = 0; + //request to root + Bit#(8) root_reqs; + + //grant from root + Bit#(8) root_grants; + + for(Integer i=0;i<8;i=i+1) + root_reqs[i] = any_req(inp[8*fromInteger(i)+7:8*fromInteger(i)]); + + root_grants = priority_encoder(root_reqs, True); + + //grants are passed back to leaves + for(Integer i=0;i<8;i=i+1) + outp[8*fromInteger(i)+7:8*fromInteger(i)] = priority_encoder(inp[8*fromInteger(i)+7:8*fromInteger(i)], unpack(root_grants[i])); + return outp; + endfunction + + Vector#(no_of_ir_pins,Array#(Reg#(Bool))) rg_ip <- replicateM(mkCReg(2,False)); + Reg#(Bool) rg_ie[v_no_of_ir_pins]; + for(Integer i = 0; i < v_no_of_ir_pins;i=i+1) + if(i==28 || i == 29) + rg_ie[i] = readOnlyReg(True); + else + rg_ie[i] <- mkReg(False); + Reg#(Bit#(32)) rg_priority_low[v_no_of_ir_pins]; + for(Integer i =0; i < v_no_of_ir_pins; i=i+1) + if(i==28 || i == 29) + rg_priority_low[i] = readOnlyReg(32'h00000001); + else + rg_priority_low[i] <- mkConfigReg(0); + Reg#(Bit#(no_of_ir_pins)) rg_priority[v_no_of_ir_pins]; + for(Integer i=0;i < v_no_of_ir_pins;i=i+1) + rg_priority[i] = concatReg2(readOnlyReg(0), rg_priority_low[i]); + Reg#(Bit#(5)) rg_priority_threshold_low <- mkReg(0); + Reg#(Bit#(priority_bits)) rg_priority_threshold = concatReg2(readOnlyReg(0),rg_priority_threshold_low); + Reg#(Bit#(priority_bits)) rg_interrupt_id <- mkConfigReg(0); + Reg#(Bool) rg_interrupt_valid <- mkConfigReg(False); + Reg#(Maybe#(Bit#(priority_bits))) rg_completion_id <- mkReg(tagged Invalid); + + rule rl_prioritise; + Bit#(priority_bits) winner_priority = 0; + Bit#(priority_bits) winner_interrupts = 0; + Bit#(x_priority_bits) ir_id_valid = 0; + Bit#(no_of_ir_pins) lv_priority = 0; + Bit#(no_of_ir_pins) lv_total_priority = 0; + for(Integer i = 0; i < v_no_of_ir_pins; i = i + 1) + begin + + if(rg_ip[i][1] && rg_ie[i]) begin + lv_priority = lv_priority | rg_priority[i]; + winner_interrupts = fromInteger(i); + `ifdef verbose $display($time,"\tInterrupt id %d and priority is %d", i, lv_priority);`endif + end + end + winner_priority = encoder(encoder_tree(lv_priority))[v_msb_priority:0]; + `ifdef verbose $display($time,"\t winner priority is %d", winner_priority);`endif + for(Integer i = 0; i < v_no_of_ir_pins; i = i + 1) begin + if(rg_priority[i][winner_priority] == 1 && rg_ip[i][1] && rg_ie[i]) + lv_total_priority[i] = 1; + end + if(lv_total_priority!=0) + winner_interrupts = encoder(encoder_tree(lv_total_priority))[v_msb_priority:0]; + if(winner_interrupts!=0) begin + ir_id_valid = encoder(rg_priority[winner_interrupts]); + if(winner_priority <= rg_priority_threshold) + begin + + `ifdef verbose $display("Interrupt valid");`endif + rg_interrupt_id <= winner_interrupts; + rg_interrupt_valid <= True; + $display($time,"\t The highest priority interrupt is %d and the priority is ", winner_interrupts, winner_priority); + end + end + endrule + + Vector#(no_of_ir_pins, Ifc_global_interrupt) temp_ifc_irq; + + for(Integer i = 0; i < v_no_of_ir_pins; i = i + 1) begin + + temp_ifc_irq[i] = interface Ifc_global_interrupt + + method Action irq_frm_gateway(Bool ir); + `ifdef verbose $display("Interrupt id %d is pending", i);`endif + rg_ip[i][0] <= True; + endmethod + + endinterface; + end + + interface ifc_external_irq = temp_ifc_irq; + +interface ifc_prog_reg = interface Ifc_program_registers; + + method ActionValue#(Bit#(data_width)) prog_reg(UncachedMemReq#(addr_width, word_size) mem_req); + //update memory mapped registers + `ifdef verbose $display($time,"\tPLIC : programming registers for address %h", mem_req.address);`endif + let address = mem_req.address; + Bit#(priority_bits) source_id=0; + Bit#(data_width) data_return = 0; +// if(address < 'h0C001000) begin + if (address < `PLICBase + 'h1000)begin + address = address >> 2; + if(mem_req.ld_st == Load) begin + source_id = address[v_msb_priority:0]; + `ifdef verbose $display($time,"\tPLIC : source %d Priority set to %h", source_id, mem_req.write_data);`endif + data_return = zeroExtend(rg_priority[source_id]); + end + else if(mem_req.ld_st == Store) begin + Bit#(no_of_ir_pins) store_data; + if(mem_req.byte_offset==0) + store_data=mem_req.write_data[v_msb_priority_bits:0]; + else + store_data=mem_req.write_data[v_data_width-1:v_data_width-v_no_of_ir_pins]; + mem_req.byte_offset = mem_req.byte_offset >> 2; + source_id = address[v_msb_priority:0] | zeroExtend(mem_req.byte_offset); + $display($time,"\tPLIC : source %d Priority set to %h", source_id, store_data); + rg_priority[source_id] <= store_data; + end + end + //else if(address < 'h0C002000) begin + else if(address<`PLICBase+'h2000)begin + if(mem_req.ld_st == Load) begin + source_id = address[v_msb_priority:0]; + source_id = source_id << 3; + for(Integer i = 0; i < 8; i = i+1) + data_return[i] = pack(rg_ip[source_id + fromInteger(i)][1]); + end + else if(mem_req.ld_st == Store) begin + source_id = zeroExtend(mem_req.byte_offset); + source_id = source_id << 3; + for(Integer i = 0; i < 8; i = i+1) begin + `ifdef verbose $display($time,"\tPLIC : pending interrupt %b id %d", mem_req.write_data[i], source_id);`endif + rg_ip[source_id + fromInteger(i)][1] <= unpack(mem_req.write_data[i]); + end + end + end + //else if(address < 'h0C020000) begin + else if(address < `PLICBase+'h20000)begin + if(mem_req.ld_st == Load) begin + source_id = address[v_msb_priority:0]; + source_id = source_id << 3; + for(Integer i = 0; i < 8; i = i+1) + data_return[i] = pack(rg_ie[source_id + fromInteger(i)]); + `ifdef verbose $display($time,"PLIC: Printing Source Enable Interrupt: %h data_return: %h",source_id,data_return); `endif + end + else if(mem_req.ld_st == Store) begin + source_id = zeroExtend(mem_req.byte_offset); + source_id = source_id << 3; + for(Integer i = 0; i < 8; i = i+1) begin + `ifdef verbose $display($time,"\tPLIC : enabled interrupt %b id %d", mem_req.write_data[i], source_id);`endif + rg_ie[source_id + fromInteger(i)] <= unpack(mem_req.write_data[i]); + end + end + end + // else if(address == 'hC200000) begin + else if(address ==`PLICBase+'h200000)begin + if(mem_req.ld_st == Load) begin + data_return = zeroExtend(rg_priority_threshold); + end + else if(mem_req.ld_st == Store) + rg_priority_threshold <= mem_req.write_data[v_msb_priority:0]; + end + // else if(address == 'hC200004) begin + else if(address == `PLICBase+'h200004)begin + if(mem_req.ld_st == Load) begin + data_return = zeroExtend(rg_interrupt_id); + rg_ip[rg_interrupt_id][1] <= False; + `ifdef verbose $display($time,"rg_ip is made false here"); `endif + end + else if(mem_req.ld_st == Store) begin + source_id = mem_req.write_data[v_msb_priority:0]; + rg_completion_id <= tagged Valid source_id; + `ifdef verbose $display("rg_completion_id is made tagged valid and completion is signaled-- source_id: %d",source_id); `endif + end + end + return data_return; + endmethod + + endinterface; + + method ActionValue#(Bit#(TLog#(no_of_ir_pins))) intrpt_completion if(isValid(rg_completion_id)); + let completion_msg = validValue(rg_completion_id); + rg_completion_id <= tagged Invalid; + `ifdef verbose $display("Sending Completion to SoC"); `endif + return completion_msg; + endmethod + + method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note; + Bool if_nmi = (rg_interrupt_id == 28 || rg_interrupt_id == 29); + Bool valid_interrupt = rg_interrupt_valid; + rg_interrupt_valid <= False; + return tuple2(valid_interrupt, if_nmi); + endmethod +endmodule + +interface Ifc_PLIC_AXI; + interface AXI4_Lite_Slave_IFC#(`PADDR, `Reg_width,`USERSPACE) axi4_slave_plic; + interface Vector#(`INTERRUPT_PINS,Ifc_global_interrupt) ifc_external_irq; + method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note; + method ActionValue#(Bit#(TLog#(`INTERRUPT_PINS))) intrpt_completion; +endinterface + +(*synthesize*) +//(*conflict_free="rl_config_plic_reg_write,intrpt_completion"*) +module mkplicperipheral(Ifc_PLIC_AXI); + +AXI4_Lite_Slave_Xactor_IFC #(`PADDR, `Reg_width, `USERSPACE) s_xactor_plic <- mkAXI4_Lite_Slave_Xactor; +Ifc_PLIC#(`PADDR, `DCACHE_WORD_SIZE, `INTERRUPT_PINS) plic <- mkplic(); + +(*preempts="rl_config_plic_reg_read, rl_config_plic_reg_write"*) + rule rl_config_plic_reg_write; + let aw <- pop_o(s_xactor_plic.o_wr_addr); + let w <- pop_o(s_xactor_plic.o_wr_data); + let w_strobe = w.wstrb; + Bit#(3) byte_offset=0; + for(Integer i=7; i >= 0; i=i-1) begin + if(w_strobe[i]==1) + byte_offset=fromInteger(i); + end + let x <- plic.ifc_prog_reg.prog_reg(UncachedMemReq{address : aw.awaddr, transfer_size : 'd3, + u_signed : 0, byte_offset : byte_offset, write_data : w.wdata, ld_st : Store}); + let w_resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_OKAY, buser: 0 }; //TODO user value is null + s_xactor_plic.i_wr_resp.enq(w_resp); + endrule + + rule rl_config_plic_reg_read; + + let ar <- pop_o(s_xactor_plic.o_rd_addr); + let x <- plic.ifc_prog_reg.prog_reg(UncachedMemReq{address : ar.araddr, transfer_size : 'd3, + u_signed : 0, byte_offset : 0, ld_st : Load}); +// if(ar.arsize==3'd0) +// x = duplicate(x[7:0]); +// else if(ar.arsize==3'd1) +// x = duplicate(x[15:0]); +// else if(ar.arsize==3'd2) + + let r = AXI4_Lite_Rd_Data {rresp: AXI4_LITE_OKAY, rdata: duplicate(x), ruser: 0}; + s_xactor_plic.i_rd_data.enq(r); + endrule + + interface axi4_slave_plic = s_xactor_plic.axi_side; + interface ifc_external_irq = plic.ifc_external_irq; + method ActionValue#(Tuple2#(Bool,Bool)) intrpt_note = plic.intrpt_note; + method ActionValue#(Bit#(TLog#(`INTERRUPT_PINS))) intrpt_completion = plic.intrpt_completion; + +endmodule +endpackage diff --git a/src/peripherals/pwm/pwm.bsv b/src/peripherals/pwm/pwm.bsv new file mode 100644 index 0000000..010db57 --- /dev/null +++ b/src/peripherals/pwm/pwm.bsv @@ -0,0 +1,364 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------------------------------------------------------------------------- + +Code inpired by the pwm module at: https://github.com/freecores/pwm + +*/ +package pwm; + `define PWMWIDTH 32 + /*=== Project imports ==*/ + import Clocks::*; + /*======================*/ + /*== Package imports ==*/ + //import defined_types::*; + `include "instance_defines.bsv" + import ClockDiv::*; + import ConcatReg::*; + import Semi_FIFOF::*; + import BUtils ::*; + `ifdef PWM_AXI4Lite + import AXI4_Lite_Types::*; + `endif + `ifdef PWM_AXI4 + import AXI4_Types::*; + `endif + /*======================*/ + + interface UserInterface; + method ActionValue#(Bool) write(Bit#(`PADDR) addr, Bit#(`Reg_width) data); + method Tuple2#(Bool, Bit#(`Reg_width)) read(Bit#(`PADDR) addr); + endinterface + + interface PWMIO; + method Bit#(1) pwm_o; + endinterface + + interface PWM; + interface UserInterface user; + interface PWMIO io; + endinterface + + (*synthesize*) + module mkPWM#(Clock ext_clock)(PWM); + + let bus_clock <- exposeCurrentClock; + let bus_reset <- exposeCurrentReset; + + Reg#(Bit#(`PWMWIDTH)) period <- mkReg(0); + Reg#(Bit#(`PWMWIDTH)) duty_cycle <- mkReg(0); + Reg#(Bit#(`PWMWIDTH)) clock_divisor <- mkReg(0); + // =========== Control registers ================== // + Reg#(Bit#(1)) clock_selector <- mkReg(0); // bit-0 + Reg#(Bit#(1)) pwm_enable <- mkReg(0); // bit-1 + Reg#(Bit#(1)) pwm_start <- mkReg(0); // bit-2 + Reg#(Bit#(1)) continous_once <- mkReg(0); // bit-3 + Reg#(Bit#(1)) pwm_output_enable <- mkReg(0); // bit-4 + Reg#(Bit#(1)) interrupt <- mkReg(0); // bit-5 + Reg#(Bit#(1)) reset_counter <- mkReg(0); // bit-7 + Reg#(Bit#(8)) control = concatReg8(reset_counter, readOnlyReg(0), + readOnlyReg(interrupt), + pwm_output_enable, continous_once, + pwm_start, pwm_enable, + clock_selector); + // ================================================ // + + // Generate a reset signal is there is a reset from + // the bus interface of if the reset_counter + // bit in the control register is set. The new reset + // is called overall_reset. Only the counter + // and the output signals need to be reset by this. + MakeResetIfc control_reset <- mkReset(1,False, bus_clock); + rule generate_reset; + if(reset_counter==1) + control_reset.assertReset; + endrule + Reset overall_reset <- mkResetEither(bus_reset,control_reset.new_rst); + + // Select between bus clock or external clock + MuxClkIfc clock_selection <- mkUngatedClockMux(ext_clock,bus_clock); + Reset async_reset <- mkAsyncResetFromCR(0,clock_selection.clock_out); + rule select_busclk_extclk; + clock_selection.select(clock_selector==1); + endrule + + // The following register is required to transfer + // the divisor value from bus_clock to + // external clock domain. This is necessary if the + // clock divider needs to operate on the + // external clock. In this case, the divisor value + // should also come from the same clock domain. + Reg#(Bit#(`PWMWIDTH)) clock_divisor_sync <- mkSyncRegFromCC(0, + clock_selection.clock_out); + rule transfer_data_from_clock_domains; + clock_divisor_sync <= clock_divisor; + endrule + + // The PWM can operate on a slowed-down clock. + // The following module generates a slowed-down + // clock based on the value given in register divisor. + // Since the clock_divider works on a muxed + // clock domain of the external clock or bus_clock, + // the divisor (which operates on the bus_clock + // will have to be synchronized and sent to the divider + Ifc_ClockDiv#(`PWMWIDTH) clock_divider <- mkClockDiv( + clocked_by clock_selection.clock_out, + reset_by async_reset); + let downclock = clock_divider.slowclock; + Reset downreset <- mkAsyncReset(0,overall_reset,downclock); + rule generate_slow_clock; + clock_divider.divisor(clock_divisor_sync); + endrule + + // ======= Actual Counter and PWM signal generation ======== // + Reg#(Bit#(1)) pwm_output <- mkReg(0,clocked_by downclock, + reset_by downreset); + Reg#(Bit#(`PWMWIDTH)) rg_counter <-mkReg(0,clocked_by downclock, + reset_by downreset); + + // create synchronizers for clock domain crossing. + Reg#(Bit#(1)) sync_pwm_output <- mkSyncRegToCC(0,downclock,downreset); + ReadOnly#(Bit#(1)) pwm_signal <- mkNullCrossingWire(bus_clock, pwm_output); + Reg#(Bit#(1)) sync_continous_once <- mkSyncRegFromCC(0,downclock); + Reg#(Bit#(`PWMWIDTH)) sync_duty_cycle <- mkSyncRegFromCC(0,downclock); + Reg#(Bit#(`PWMWIDTH)) sync_period <- mkSyncRegFromCC(0,downclock); + Reg#(Bit#(1)) sync_pwm_enable <- mkSyncRegFromCC(0,downclock); + Reg#(Bit#(1)) sync_pwm_start <- mkSyncRegFromCC(0,downclock); + rule sync_pwm_output_to_default_clock; + sync_pwm_output <= pwm_output; + endrule + + // capture the synchronized values from the default + // clock domain to the downclock domain for + // actual timer and pwm functionality. + rule sync_from_default_to_downclock; + sync_continous_once <= continous_once; + sync_duty_cycle <= duty_cycle; + sync_period <= period; + sync_pwm_enable <= pwm_enable; + sync_pwm_start <= pwm_start; + endrule + let temp = sync_period==0?0:sync_period-1; + + // This rule generates the interrupt in the timer + // mode and resets it if the user-write interface + // writes a value of 1 to the reset_counter bit. + rule generate_interrupt_in_timer_mode; + if(pwm_enable==0) + interrupt <= sync_pwm_output; + else if(reset_counter==1) + interrupt <= 0; + else + interrupt <= 0; + endrule + + // This rule performs the actual pwm and the timer + // functionality. if pwm_enable is 1 then the + // PWM mode is selected. Every time the counter + // value equals/crosses the period value it is + // reset and the output pwm_output signal is toggled. + // The timer mode is selected when pwm_enable is 0. + // Here again 2 more modes are possible. if the + // continous_once bit is 0 then the timer is in one time. + // In this case once the counter reaches + // the period value it raises an interrupt and + // stops the counter. In the continuous mode + // however, when the counter reaches the period value + // the interrupt is raise, the counter is + // reset to 0 and continues counting. + // During continuous counting the interrupt can be cleared by + // the user but will be set back when the counter reaches the period value. + rule compare_and_generate_pwm(sync_pwm_start==1); + let cntr = rg_counter+1; + if(sync_pwm_enable==1)begin // PWM mode enabled + if(rg_counter >= temp) + rg_counter <= 0; + else + rg_counter <= cntr; + if(rg_counter < sync_duty_cycle) + pwm_output <= 1; + else + pwm_output <= 0; + end + else begin // Timer mode enabled. + if(sync_continous_once==0) begin // One time mode. + if(rg_counter >= temp)begin + pwm_output <= 1; + end + else + rg_counter <= cntr; + end + else begin // Continous mode. + if(rg_counter >= temp)begin + pwm_output <= 1; + rg_counter <= 0; + end + else begin + rg_counter <= cntr; + end + end + end + endrule + + // ========================================================= // + interface user = interface UserInterface + method ActionValue#(Bool) write(Bit#(`PADDR) addr, Bit#(`Reg_width) data); + Bool err = False; + case(addr[4:2]) + 0: period <= truncate(data); + 1: duty_cycle <= truncate(data); + 2: begin control <= truncate(data);end + 3: clock_divisor <= truncate(data); + default: err = True; + endcase + return err; + endmethod + + method Tuple2#(Bool, Bit#(`Reg_width)) read(Bit#(`PADDR) addr); + Bool err = False; + Bit#(`Reg_width) data; + case(addr[4:2]) + 0: data = zeroExtend(period); + 1: data = zeroExtend(duty_cycle); + 2: data = zeroExtend(control); + 3: data = zeroExtend(clock_divisor); + default: begin err = True; data = 0; end + endcase + return tuple2(err,data); + endmethod + endinterface; + interface io = interface PWMIO + method pwm_o=pwm_output_enable==1?pwm_signal:0; + endinterface; + endmodule + + `ifdef PWM_AXI4Lite + // the following interface and module will add the + // AXI4Lite interface to the PWM module + interface Ifc_PWM_bus; + interface PWMIO pwm_io; + interface AXI4_Lite_Slave_IFC#(`PADDR, `Reg_width, + `USERSPACE) axi4_slave; + endinterface + + (*synthesize*) + module mkPWM_bus#(Clock ext_clock)(Ifc_PWM_bus); + PWM pwm <-mkPWM(ext_clock); + AXI4_Lite_Slave_Xactor_IFC#(`PADDR,`Reg_width, `USERSPACE) + s_xactor<-mkAXI4_Lite_Slave_Xactor(); + + rule read_request; + let req <- pop_o (s_xactor.o_rd_addr); + let {err,data} = pwm.user.read(req.araddr); + let resp= AXI4_Lite_Rd_Data {rresp:err? + AXI4_LITE_SLVERR:AXI4_LITE_OKAY, + rdata:data, ruser: ?}; + s_xactor.i_rd_data.enq(resp); + endrule + + rule write_request; + let addreq <- pop_o(s_xactor.o_wr_addr); + let datareq <- pop_o(s_xactor.o_wr_data); + let err <- pwm.user.write(addreq.awaddr, datareq.wdata); + let resp = AXI4_Lite_Wr_Resp {bresp: err? + AXI4_LITE_SLVERR:AXI4_LITE_OKAY, + buser: ?}; + s_xactor.i_wr_resp.enq(resp); + endrule + + interface pwm_io = pwm.io; + interface axi4_slave = s_xactor.axi_side; + endmodule + `endif + + `ifdef PWM_AXI4 + // the following interface and module will add the + // AXI4 interface to the PWM module + interface Ifc_PWM_bus; + interface PWMIO pwm_io; + interface AXI4_Slave_IFC#(`PADDR, `Reg_width,`USERSPACE) axi4_slave; + endinterface + + (*synthesize*) + module mkPWM_bus#(Clock ext_clock)(Ifc_PWM_bus); + PWM pwm <-mkPWM(ext_clock); + AXI4_Slave_Xactor_IFC#(`PADDR,`Reg_width, + `USERSPACE) s_xactor<-mkAXI4_Slave_Xactor(); + + rule read_request; + let req <- pop_o (s_xactor.o_rd_addr); + let {err,data} = pwm.user.read(req.araddr); + if(!(req.arsize == 2 && req.arlen == 0)) + err = True; + let resp= AXI4_Rd_Data {rresp:err?AXI4_SLVERR:AXI4_OKAY, + rdata:data, ruser: + ?, rid:req.arid, rlast: True}; + s_xactor.i_rd_data.enq(resp); + endrule + + rule write_request; + let addreq <- pop_o(s_xactor.o_wr_addr); + let datareq <- pop_o(s_xactor.o_wr_data); + let err <- pwm.user.write(addreq.awaddr, datareq.wdata); + if(!(addreq.awsize == 2 && addreq.awlen == 0)) + err = True; + let resp = AXI4_Wr_Resp {bresp: err?AXI4_SLVERR:AXI4_OKAY, buser: ?, + bid:datareq.wid}; + s_xactor.i_wr_resp.enq(resp); + endrule + + interface pwm_io = pwm.io; + endmodule + `endif + `ifdef PWM_TEST + (*synthesize*) + module mkTb(Empty); + let clk <- exposeCurrentClock; + PWM pwm <- mkPWM(clk, 32); + Reg#(Bit#(5)) rg_state <- mkReg(0); + + rule state1(rg_state==0); + rg_state<=1; + let x <- pwm.user.write(0,'d4); + endrule + rule state2(rg_state==1); + rg_state<=2; + let x <- pwm.user.write('d4,'d3); + endrule + rule state3(rg_state==2); + rg_state<=3; + let x <- pwm.user.write('hc,'d4); + endrule + rule state4(rg_state==3); + rg_state<=4; + let x <- pwm.user.write(8,'b0001_0110); + endrule + endmodule + `endif +endpackage diff --git a/src/peripherals/qspi/qspi.bsv b/src/peripherals/qspi/qspi.bsv new file mode 100644 index 0000000..47e79e3 --- /dev/null +++ b/src/peripherals/qspi/qspi.bsv @@ -0,0 +1,1320 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ +package qspi; +/* + TODOs + To use the following registers: + dcr_csht +* Done Pre-scaler. +> Memory mapped mode should continue to fetch data and fill the fifo even if the Core + is not requesting +* Done Status polling mode. +> This could not be replicated since the tof flag is set. + Memory mapped mode with dcyc =10 creates an extra cycle after Dummy phase. +> Data Read phase is on posedge and Data write is on negdege +-- send the received arid and bid's so that DMA can identify. duplicate instead of extend +*/ + import TriState::*; + import ConcatReg ::*; + import Semi_FIFOF :: *; + import AXI4_Lite_Types :: *; + import AXI4_Lite_Fabric :: *; + import FIFO::*; + import FIFOF::*; + import SpecialFIFOs::*; + import MIMO::*; + import DefaultValue :: *; + `include "instance_defines.bsv" + `include "qspi.defs" + import ConfigReg::*; + import Vector::*; + import UniqueWrappers :: * ; + import DReg::*; + import BUtils::*; + (*always_ready, always_enabled*) + interface QSPI_out; + /*(* always_ready, result="clk_o" *) */ method bit clk_o; + /*(* always_ready, result="io_o" *) */ method Bit#(4) io_o; + /*(* always_ready, result="io0_sdio_ctrl" *) */ method Bit#(9) io0_sdio_ctrl; + /*(* always_ready, result="io1_sdio_ctrl" *) */ method Bit#(9) io1_sdio_ctrl; + /*(* always_ready, result="io2_sdio_ctrl" *) */ method Bit#(9) io2_sdio_ctrl; + /*(* always_ready, result="io3_sdio_ctrl" *) */ method Bit#(9) io3_sdio_ctrl; + /*(* always_ready, result="io_enable" *)*/ method Bit#(4) io_enable; + /*(* always_ready, always_enabled *) */ method Action io_i ((* port="io_i" *) Bit#(4) io_in); // in + /*(* always_ready, result="ncs_o" *) */ method bit ncs_o; + endinterface + + interface Ifc_qspi; + interface QSPI_out out; + interface AXI4_Lite_Slave_IFC#(`PADDR,`Reg_width,`USERSPACE) slave; + method Bit#(6) interrupts; // 0=TOF, 1=SMF, 2=Threshold, 3=TCF, 4=TEF 5 = request_ready +`ifdef simulate + method Phase curphase; +`endif + endinterface + + function Reg#(t) readOnlyReg(t r); + return (interface Reg; + method t _read = r; + method Action _write(t x) = noAction; + endinterface); + endfunction + + function Reg#(t) conditionalWrite(Reg#(t) r, Bool a); + return (interface Reg; + method t _read = r._read; + method Action _write(t x); + if(a) + r._write(x); + endmethod + endinterface); + endfunction + + function Reg#(t) clearSideEffect(Reg#(t) r, Action a, Action b) + provisos( Literal#(t),Eq#(t)); + return (interface Reg; + method t _read = r._read; + method Action _write(t x); + r._write(x); + if(x==1) begin + a; + b; + end + endmethod + endinterface); + endfunction + + function Reg#(Bit#(32)) writeSideEffect(Reg#(Bit#(32)) r, Action a); + return (interface Reg; + method Bit#(32) _read = r._read; + method Action _write(Bit#(32) x); + r._write(x); + a; + endmethod + endinterface); + endfunction + + function Reg#(Bit#(n)) writeCCREffect(Reg#(Bit#(n)) r, Action a, Action b); + return (interface Reg; + method Bit#(n) _read = r._read; + method Action _write(Bit#(n) x); + r._write(x); + `ifdef verbose1 $display("x: %h",x); `endif + if(x[11:10]==0 && (x[27:26] == 'b00 || x[27:26]=='b01 || x[25:24]=='b0) && x[9:8]!=0) begin // no address required and nodata from firmware (i.e. no write) + a; + end + if(x[27:26]=='b11) //Memory Mapped Mode + b; + endmethod + endinterface); + endfunction + + typedef enum {Instruction_phase=0, + Address_phase=1, + AlternateByte_phase=2, + Dummy_phase=3, + DataRead_phase=4, + DataWrite_phase=5, + Idle=6} Phase deriving (Bits,Eq,FShow); + + (*synthesize*) + module mkqspi(Ifc_qspi); + + AXI4_Lite_Slave_Xactor_IFC #(`PADDR, `Reg_width, `USERSPACE) s_xactor <- mkAXI4_Lite_Slave_Xactor; + /*************** List of implementation defined Registers *****************/ + Reg#(bit) rg_clk <-mkReg(1); + Reg#(Bit#(8)) rg_clk_counter<-mkReg(0); + MIMOConfiguration cfg=defaultValue; + cfg.unguarded=True; + MIMO#(4,4,16,Bit#(8)) fifo <-mkMIMO(cfg); + Reg#(Phase) rg_phase <-mkReg(Idle); + Reg#(Phase) rg_phase_delayed <-mkReg(Idle); + Reg#(Bit#(4)) rg_output <-mkReg(0); + Reg#(Bit#(4)) rg_output_en <-mkReg(0); + Reg#(Bool) rg_input_en <-mkReg(False); + Wire#(Bit#(4)) rg_input <-mkDWire(0); + Reg#(Bit#(32)) rg_count_bits <-mkReg(0); // count bits to be transfered + Reg#(Bit#(32)) rg_count_bytes <-mkReg(0); // count bytes to be transfered + Wire#(Bool) wr_sdr_clock <-mkDWire(False); // use this to trigger posedge of sclk + Reg#(Bool) wr_sdr_delayed <- mkReg(False); + Reg#(Bool) wr_instruction_written<-mkDReg(False); // this wire is se when the instruction is written by the AXI Master + Reg#(Bool) wr_address_written<-mkDReg(False); // this wire is set when the address is written by the AXI Master + Reg#(Bool) wr_read_request_from_AXI<-mkDReg(False); // this wire is set when the address is written by the AXI Master + Reg#(Bool) wr_data_written<-mkDReg(False); // this wire is set when the data is written by the AXI Master + Reg#(Bool) instruction_sent<-mkReg(False); // This register is set when the instruction has been sent once to the flash + Reg#(Bit#(1)) ncs <-mkReg(1); // this is the chip select + Reg#(Bit#(1)) delay_ncs <-mkReg(1); // this is the chip select + Wire#(Bool) wr_status_read<-mkDWire(False); // this wire is set when the status register is written + Wire#(Bool) wr_data_read<-mkDWire(False); // this wire is set when the data register is written + Reg#(Bool) half_cycle_delay<-mkReg(False); + Reg#(Bit#(16)) timecounter<-mkReg(0); + Reg#(Bool) read_true <- mkReg(False); + Reg#(Bool) first_read <- mkReg(False); + /*************** End of implementation defined Registers *****************/ + + /*************** List of QSPI defined Registers *****************/ + Reg#(Bit#(1)) sr_busy <-mkConfigReg(0); // set when the operation is in progress. + Reg#(Bit#(5)) sr_flevel <-mkReg(0); // FIFO Level. Number of valid bytes held in the FIFO. 0: empty + Reg#(Bit#(1)) sr_tof <-mkReg(0); // set when the timeout occurs. + Reg#(Bit#(1)) sr_smf <-mkReg(0); // set when the unmasked receieved data matches psmar. + Reg#(Bit#(1)) sr_ftf <-mkReg(0); // set when the FIFO threshold is reached. + Reg#(Bit#(1)) sr_tcf <-mkReg(0); // set when programmed number of data has been transfered or when aborted. + Reg#(Bit#(1)) delay_sr_tcf <-mkReg(0); // set when programmed number of data has been transfered or when aborted. + Reg#(Bit#(1)) sr_tef <-mkReg(0); // set when an error occurs on transfer. + Reg#(Bit#(32)) sr = concatReg9(readOnlyReg(19'd0),readOnlyReg(sr_flevel),readOnlyReg(2'd0),readOnlyReg(sr_busy),readOnlyReg(sr_tof),readOnlyReg(sr_smf),readOnlyReg(sr_ftf),readOnlyReg(sr_tcf),readOnlyReg(sr_tef)); + + + Reg#(Bit#(8)) prescaler<-mkReg(0); + Reg#(Bit#(8)) cr_prescaler=conditionalWrite(prescaler,sr_busy==0); // prescaler register part of the control register. + Reg#(Bit#(1)) pmm <-mkReg(0); + Reg#(Bit#(1)) cr_pmm =conditionalWrite(pmm,sr_busy==0); // polling match mode. 0: AND match and 1: OR match. + Reg#(Bit#(1)) apms <-mkReg(0); + Reg#(Bit#(1)) cr_apms =conditionalWrite(apms,sr_busy==0); // automatic poll mode stop. 1: stop when match. 0: stopped by disabling qspi. + Reg#(Bit#(1)) cr_toie <-mkReg(0); // enabled interrupt on time-out. + Reg#(Bit#(1)) cr_smie <-mkReg(0); // enables status match interrupt. + Reg#(Bit#(1)) cr_ftie <-mkReg(0); // enables interrupt on FIFO threshold. + Reg#(Bit#(1)) cr_tcie <-mkReg(0); // enables interrupt on completion of transfer. + Reg#(Bit#(1)) cr_teie <-mkReg(0); // enables interrupt on error of transfer. + Reg#(Bit#(4)) cr_fthres<-mkReg(0); // defines the number of bytes in the FIFO that will cause the FTF in sr to be raised. + Reg#(Bit#(1)) fsel<-mkReg(0); + Reg#(Bit#(1)) cr_fsel=conditionalWrite(fsel,sr_busy==0); // used for flash memory selection TODO: Not required. + Reg#(Bit#(1)) dfm<-mkReg(0); + Reg#(Bit#(1)) cr_dfm =conditionalWrite(dfm,sr_busy==0); // used for dual flash mode TODO: Not required. + Reg#(Bit#(1)) sshift<-mkReg(0); + Reg#(Bit#(1)) cr_sshift =conditionalWrite(sshift,sr_busy==0); // sample shift to account for delays from the flash. TODO: Might not be required. + Reg#(Bit#(1)) tcen<-mkReg(0); + Reg#(Bit#(1)) cr_tcen =conditionalWrite(tcen,sr_busy==0); // enables the timeout counter. + Reg#(Bit#(1)) cr_dmaen <- mkReg(0); // enables the dma transfer. + Reg#(Bit#(1)) cr_abort <- mkReg(0); // this bit aborts the ongoing transaction. + Reg#(Bit#(1)) cr_en <-mkReg(0); // this bit enables the qspi. + Reg#(Bit#(32)) cr=concatReg19(cr_prescaler,cr_pmm,cr_apms,readOnlyReg(1'b0),cr_toie,cr_smie,cr_ftie,cr_tcie,cr_teie,readOnlyReg(4'd0),cr_fthres,cr_fsel,cr_dfm,readOnlyReg(1'b0),cr_sshift,cr_tcen,cr_dmaen,cr_abort,cr_en); + + Reg#(Bit#(5)) fsize<-mkReg(0); + Reg#(Bit#(5)) dcr_fsize =conditionalWrite(fsize,sr_busy==0); // flash memory size. + Reg#(Bit#(3)) csht <-mkReg(0); + Reg#(Bit#(3)) dcr_csht = conditionalWrite(csht,sr_busy==0); // chip select high time. + Reg#(Bit#(1)) ckmode <-mkReg(0); + Reg#(Bit#(1)) dcr_ckmode =conditionalWrite(ckmode,sr_busy==0); // mode 0 or mode 3. + Reg#(Bit#(8)) dcr_mode_byte <- mkReg(0); + Reg#(Bit#(32)) dcr = concatReg7(readOnlyReg(3'd0),dcr_mode_byte,dcr_fsize,readOnlyReg(5'd0),dcr_csht,readOnlyReg(7'd0),dcr_ckmode); + Reg#(Bit#(32)) rg_mode_bytes = concatReg2(dcr_mode_byte,readOnlyReg(24'd0)); + Reg#(Bit#(5)) rg_mode_byte_counter <- mkReg('d31); + + Reg#(Bit#(1)) fcr_ctof <-mkReg(0); // writing 1 clears the sr_tof flag. + Reg#(Bit#(1)) fcr_csmf <-mkReg(0); // writing 1 clears the sr_smf flag. + Reg#(Bit#(1)) fcr_ctcf <-mkReg(0); // writing 1 clears the sr_tcf flag. + Reg#(Bit#(1)) fcr_ctef <-mkReg(0); // writing 1 clears the sr_tef flag. + Reg#(Bit#(32)) fcr=concatReg6(readOnlyReg(27'd0),clearSideEffect(fcr_ctof,sr_tof._write(0),noAction),clearSideEffect(fcr_csmf,sr_smf._write(0),noAction),readOnlyReg(1'b0),clearSideEffect(fcr_ctcf,sr_tcf._write(0),delay_sr_tcf._write(0)),clearSideEffect(fcr_ctef,sr_tef._write(0),noAction)); + + Reg#(Bit#(32)) data_length<-mkReg(0); + Reg#(Bit#(32)) dlr=conditionalWrite(data_length,sr_busy==0); // data length register + + Reg#(Bit#(1)) ddrm<-mkReg(0); + Reg#(Bit#(1)) ccr_ddrm =conditionalWrite(ddrm,sr_busy==0); // double data rate mode. + Reg#(Bit#(1)) dhhc <-mkReg(0); + Reg#(Bit#(1)) ccr_dhhc =conditionalWrite(dhhc,sr_busy==0); // delay output by 1/4 in DDR mode. TODO: Not required. + Reg#(Bit#(1)) sioo <-mkReg(0); + Reg#(Bit#(1)) ccr_sioo =conditionalWrite(sioo,sr_busy==0); // send instruction based on mode selected. + Reg#(Bit#(2)) fmode <-mkReg(0); + Reg#(Bit#(2)) ccr_fmode =conditionalWrite(fmode,sr_busy==0); // 00: indirect Read, 01: indirect Write, 10: Auto polling, 11: MMapped. + Reg#(Bit#(2)) dmode <-mkReg(0); + Reg#(Bit#(2)) ccr_dmode =conditionalWrite(dmode,sr_busy==0); // data mode. 01: single line, 10: two line, 11: four lines. + Reg#(Bit#(5)) dcyc <-mkReg(0); + Reg#(Bit#(5)) ccr_dcyc =conditionalWrite(dcyc,sr_busy==0); // number of dummy cycles. + Reg#(Bit#(2)) absize <-mkReg(0); + Reg#(Bit#(2)) ccr_absize=conditionalWrite(absize,sr_busy==0); // number of alternate byte sizes. + Reg#(Bit#(2)) abmode <-mkReg(0); + Reg#(Bit#(2)) ccr_abmode=conditionalWrite(abmode,sr_busy==0); // alternate byte mode. + Reg#(Bit#(2)) adsize <-mkReg(0); + Reg#(Bit#(2)) ccr_adsize=conditionalWrite(adsize,sr_busy==0); // address size. + Reg#(Bit#(2)) admode <-mkReg(0); + Reg#(Bit#(2)) ccr_admode=conditionalWrite(admode,sr_busy==0); // address mode. + Reg#(Bit#(2)) imode <-mkReg(0); + Reg#(Bit#(2)) ccr_imode =conditionalWrite(imode,sr_busy==0); // instruction mode. + Reg#(Bit#(8)) instruction <-mkReg(0); + Reg#(Bit#(8)) ccr_instruction =conditionalWrite(instruction,sr_busy==0); // instruction to be sent externally. + Reg#(Bit#(1)) ccr_dummy_confirmation <- mkReg(0); //Programming Dummy confirmation bit needed by Micron model to trigger XIP mode + Reg#(Bit#(1)) ccr_dummy_bit <- mkReg(0); //Dummy bit to be sent + Reg#(Bit#(32)) ccr =writeCCREffect(concatReg14(ccr_ddrm,ccr_dhhc,ccr_dummy_bit,ccr_sioo,ccr_fmode,ccr_dmode,ccr_dummy_confirmation,ccr_dcyc,ccr_absize,ccr_abmode,ccr_adsize,ccr_admode,ccr_imode,ccr_instruction),wr_instruction_written._write(True),first_read._write(True)); + + Reg#(Bit#(32)) mm_data_length <-mkConfigReg(0); + Reg#(Bit#(28)) mm_address <-mkConfigReg(0); + Reg#(Bit#(28)) rg_prev_addr <- mkConfigReg(0); + Reg#(Bit#(32)) rg_address <-mkReg(0); + Reg#(Bit#(32)) ar =conditionalWrite(writeSideEffect(rg_address,wr_address_written._write(True)),sr_busy==0 && ccr_fmode!='b11); // address register + + Reg#(Bit#(32)) rg_alternatebyte_reg<-mkReg(0); + Reg#(Bit#(32)) abr=conditionalWrite(rg_alternatebyte_reg,sr_busy==0); // alternate byte register + + Reg#(Bit#(32)) rg_data <-mkReg(0); + Reg#(Bit#(32)) dr =writeSideEffect(rg_data,wr_data_written._write(True)); // data register + + Reg#(Bit#(32)) rg_psmkr <-mkReg(0); + Reg#(Bit#(32)) psmkr =conditionalWrite(rg_psmkr,sr_busy==0); // polling status mask register + + Reg#(Bit#(32)) rg_psmar <-mkReg(0); + Reg#(Bit#(32)) psmar =conditionalWrite(rg_psmar,sr_busy==0); // polling statue match register + + Reg#(Bit#(16)) pir_interval <-mkReg(0); // polling interval + Reg#(Bit#(32)) pir =conditionalWrite(concatReg2(readOnlyReg(16'd0),pir_interval),sr_busy==0); // polling interval register + + Reg#(Bit#(16)) lptr_timeout <-mkReg(0); // timeout period + Reg#(Bit#(32)) lptr =conditionalWrite(concatReg2(readOnlyReg(16'd0),lptr_timeout),sr_busy==0); // low power timeout register. + Reg#(Bool) thres <- mkReg(False); + Reg#(Bit#(32)) sdio0r <- mkReg(32'h00000073); + Reg#(Bit#(32)) sdio1r <- mkReg(32'h00000073); + Reg#(Bit#(32)) sdio2r <- mkReg(32'h00000073); + Reg#(Bit#(32)) sdio3r <- mkReg(32'h00000073); + Reg#(Bool) rg_request_ready <- mkReg(True); + Bool ddr_clock = ((wr_sdr_clock&&!wr_sdr_delayed)||(!wr_sdr_clock&&wr_sdr_delayed)); + Bool transfer_cond = (sr_busy==1 && cr_abort==0 && cr_en==1); + Bool clock_cond = ((wr_sdr_clock && ccr_ddrm==0) || (ddr_clock && ccr_ddrm==1)); + Bool qspi_flush = (cr_abort==1 || cr_en==0); + /*************** End of QSPI defined Registers *****************/ + function Reg#(Bit#(32)) access_register(Bit#(8) address); + Reg#(Bit#(32)) register=( + case(address) + `CR : cr; + `DCR : dcr; + `FCR : fcr; + `DLR : dlr; + `CCR : ccr; + `AR : ar; + `ABR : abr; + `DR : dr; + `SR : sr; + `PSMKR : psmkr; + `PSMAR : psmar; + `PIR : pir; + `LPTR : lptr; + `SDIO0 : sdio0r; + `SDIO1 : sdio1r; + `SDIO2 : sdio2r; + `SDIO3 : sdio3r; + default: readOnlyReg(0); + endcase + ); + return register; + endfunction + + /* This function defines the next phase that needs to be executed. indicates if + the operation is over and also the value of rg_count_bits for the next phase*/ + function Tuple3#(Bit#(32),Bit#(1),Phase) phase_change(Phase current_phase, Bit#(32) count_val, Bit#(1) smf); + Phase next_phase=Idle; + if(current_phase==Idle) + next_phase=Instruction_phase; + if(current_phase==Instruction_phase) + next_phase=Address_phase; + if(current_phase==Address_phase) + next_phase=AlternateByte_phase; + if(current_phase==AlternateByte_phase) + next_phase=Dummy_phase; + if(current_phase==Dummy_phase) + next_phase=(ccr_fmode=='b00)?DataWrite_phase:DataRead_phase; + if(current_phase==DataRead_phase)begin + if(ccr_fmode=='b01 || ccr_fmode=='b10) // indirect modes + next_phase=Idle; + else if(ccr_fmode=='b10) // auto-status polling mode + if(smf==1) + next_phase=Idle; + else + next_phase=Dummy_phase; + else + next_phase=DataRead_phase; //Memory Mapped mode + end + if(current_phase==DataWrite_phase) + next_phase=Idle; + + if(next_phase==Instruction_phase && (ccr_imode==0||(ccr_sioo==1 && instruction_sent))) // if single instruction mode or no instruction mode + next_phase=Address_phase; + if(next_phase==Address_phase && ccr_admode==0) + next_phase=AlternateByte_phase; + if(next_phase==AlternateByte_phase && ccr_abmode==0) + next_phase=Dummy_phase; + if(next_phase==Dummy_phase && ccr_dcyc==0) + next_phase=ccr_fmode==0?DataWrite_phase:DataRead_phase; + if(next_phase==Dummy_phase && (ccr_fmode=='b10 && pir_interval==0))begin // TODO Check if this is correct or needs more logic. + next_phase=Instruction_phase; + end + if((next_phase==DataWrite_phase || next_phase==DataRead_phase) && ccr_dmode==0 && ccr_fmode!='b11)begin + if(ccr_fmode=='b01 || ccr_fmode=='b00) + next_phase=Idle; + else if(ccr_fmode=='b10) + if(smf==1) + next_phase=Idle; + else + next_phase=Dummy_phase; + end + + if(next_phase==Instruction_phase)begin + count_val=8; + end + if(next_phase==Address_phase)begin + count_val=(ccr_fmode=='b11)?32:(case(ccr_adsize) 0:8; 1:16; 2:24; 3:32; endcase); + end + if(next_phase==AlternateByte_phase)begin + count_val=(case(ccr_absize) 0:8; 1:16; 2:24; 3:32; endcase); + end + if(next_phase==Dummy_phase)begin + count_val=(ccr_fmode=='b10)? zeroExtend(pir_interval):zeroExtend(ccr_dcyc); + end + if(next_phase==DataWrite_phase)begin + count_val=8; + end + if(next_phase==DataRead_phase)begin + count_val=0; + end + Bit#(1) tcf=0; + if(current_phase!=Idle && next_phase==Idle && (ccr_fmode=='b00 || ccr_fmode=='b01))begin // only in indirect mode raise completion of transfer TODO remove ccr_fmode=='b11 from this line. + tcf=1; + end + return tuple3(count_val,tcf,next_phase); + endfunction + + Wrapper3#(Phase,Bit#(32),Bit#(1),Tuple3#(Bit#(32),Bit#(1),Phase)) change_phase<-mkUniqueWrapper3(phase_change); + /* This rule receives the write request from the AXI and updates the relevant + QSPI register set using the lower 12 bits as address map */ + rule rl_write_request_from_AXI; + let aw <- pop_o (s_xactor.o_wr_addr); + let w <- pop_o (s_xactor.o_wr_data); + AXI4_Lite_Resp axi4_bresp = AXI4_LITE_OKAY; + if(ccr_fmode=='b11 && aw.awaddr[7:0]==`DR) begin //Undefined behavior when written into integral fields in CR, CCR!!! + axi4_bresp = AXI4_LITE_SLVERR; + `ifdef verbose $display("Sending AXI4_LITE_SLVERR because store in memory mapped mode and not clearing Interrupt Flags"); `endif + end + `ifdef verbose $display($time,"\tReceived AXI write request to Address: %h Data: %h Size: %h",aw.awaddr,w.wdata,aw.awsize); `endif + if(aw.awaddr[7:0]==`DR)begin + if(aw.awsize==0)begin + dr[7:0]<=w.wdata[7:0]; + Vector#(4,Bit#(8)) temp=newVector(); + temp[0]=w.wdata[7:0]; + if(fifo.enqReadyN(1)) + fifo.enq(1,temp); + end + else if(aw.awsize==1)begin + dr[15:0]<=w.wdata[15:0]; + Vector#(4,Bit#(8)) temp = newVector(); + temp[0]=w.wdata[7:0]; + temp[1]=w.wdata[15:8]; + if(fifo.enqReadyN(2)) + fifo.enq(2,temp); + end + else if(aw.awsize==2)begin + dr<=w.wdata[31:0]; + Vector#(4,Bit#(8)) temp = newVector(); + temp[3]=w.wdata[31:24]; + temp[2]=w.wdata[23:16]; + temp[1]=w.wdata[15:8]; + temp[0]=w.wdata[7:0]; + if(fifo.enqReadyN(4)) + fifo.enq(4,temp); + end + else begin + axi4_bresp = AXI4_LITE_SLVERR; + `ifdef verbose $display("Sending AXI4_LITE_SLVERR because DR awsize is 64-bit"); `endif + end + end + else begin + let reg1=access_register(aw.awaddr[7:0]); + `ifdef verbose $display("Write Reg access: %h Write Data: %h Size: %h",aw.awaddr[7:0],w.wdata,aw.awsize); `endif + //Byte and Half-Word Writes are not permitted in ConfigReg Space + if(aw.awsize==2) // 32 bits + reg1<=w.wdata[31:0]; + else begin + axi4_bresp = AXI4_LITE_SLVERR; + `ifdef verbose $display("Sending SLVERR because Accessed register's awsize was different"); `endif + end + end + + let b = AXI4_Lite_Wr_Resp {bresp: axi4_bresp, buser: aw.awuser}; + s_xactor.i_wr_resp.enq (b); + endrule + + /* This rule receives the read request from the AXI and responds with the relevant + QSPI register set using the lower 12 bits as address map */ + (*descending_urgency="rl_read_request_from_AXI,rl_write_request_from_AXI"*) //experimental + rule rl_read_request_from_AXI(rg_request_ready==True); + let axir<- pop_o(s_xactor.o_rd_addr); + Bool request_ready = True; + `ifdef verbose $display($time,"\tReceived AXI read request to Address: %h Size: %h",axir.araddr,axir.arsize); `endif + if((axir.araddr[27:0]>=`STARTMM && axir.araddr[27:0]<=`ENDMM) && axir.araddr[31]==1'b1)begin // memory mapped space + + wr_read_request_from_AXI<=True; //Could this lead to some error? Need to think about this, without fail + AXI4_Lite_Resp axi4_rresp = AXI4_LITE_OKAY; + mm_address<=truncate(axir.araddr); + Bit#(4) data_length = axir.arsize==0?1:axir.arsize==1?2:axir.arsize==2?4:8; + mm_data_length<= zeroExtend(data_length); + Bit#(28) address_limit = 1 << dcr_fsize; + + //It is forbidden to access the flash bank area before the SPI is properly configured -- fmode is '11?? + //If not sending a SLVERR now if the mode is not memory mapped and if an access is made outside allowed + if(ccr_fmode!='b11 || axir.araddr[27:0] > address_limit) begin + `ifdef verbose $display("Sending Slave Error ccr_fmode: %h mm_address: %h address_limit: %h dcr_fsize: %h",ccr_fmode,mm_address,address_limit, dcr_fsize); `endif + axi4_rresp = AXI4_LITE_SLVERR; + let r = AXI4_Lite_Rd_Data {rresp: axi4_rresp, rdata: 0 , ruser: 0}; + s_xactor.i_rd_data.enq(r); + axi4_rresp = AXI4_LITE_SLVERR; + rg_phase <= Idle; //Will this work? + cr_en <= 0; + sr_busy <= 0; + ncs <= 1; //Just resetting all the parameters, just in case. Should Ask Neel + first_read <= True; + end + else if(sr_busy==1 ||thres) begin //Bus is busy with Memory mapped maybe? + `ifdef verbose $display($time,"sr_busy: %d, thres: %d rg_prev_addr: %h axir.araddr: %h fifo_count: %d", sr_busy, thres, rg_prev_addr, axir.araddr, fifo.count); `endif + Bit#(28) eff_addr = rg_prev_addr + zeroExtend(data_length); + if((eff_addr!= truncate(axir.araddr)) || pack(fifo.count)==0 || ccr_dummy_bit==1'b1) begin + `ifdef verbose $display($time,"Not Equal eff_addr: %h mm_address : %h axir.araddr: %h rg_prev_addr: %h data_length : %h sum : %h fifo.count: %h ccr_dummy_bit: %h",eff_addr,mm_address,axir.araddr,rg_prev_addr,data_length,rg_prev_addr+zeroExtend(data_length),pack(fifo.count),ccr_dummy_bit); `endif + sr_busy<=0; + rg_phase<=Idle; + ncs<=1; + fifo.clear(); + thres <= False; + //$display($time,"Setting Thres to FALSE"); + first_read <= True; + request_ready = False; + end + else if(!first_read) begin + request_ready = True; + rg_prev_addr <= truncate(axir.araddr); + Bit#(32) reg1 = 0; + if(axir.arsize==0) begin // 8 bits + if(fifo.deqReadyN(1))begin + let temp=fifo.first[0]; + reg1=duplicate(temp); + fifo.deq(1); + end + end + else if(axir.arsize==1) begin // 16 bits + if(fifo.deqReadyN(2)) begin + let temp={fifo.first[0],fifo.first[1]}; + reg1=duplicate(temp); + fifo.deq(2); + end + end + else if(axir.arsize==2) begin // 32 bits + if(fifo.deqReadyN(4)) begin + let temp={fifo.first[0],fifo.first[1],fifo.first[2],fifo.first[3]}; + reg1=duplicate(temp); + fifo.deq(4); + end + end + else + axi4_rresp = AXI4_LITE_SLVERR; + `ifdef verbose $display("Sending Response to the core: reg1: %h", reg1); `endif + let r = AXI4_Lite_Rd_Data {rresp: axi4_rresp, rdata: duplicate(reg1) , ruser: 0}; + s_xactor.i_rd_data.enq(r); + end + end + end + else begin + let reg1=access_register(axir.araddr[7:0]); + `ifdef verbose $display("Reg Read Access: %h arsize: %h",axir.araddr[7:0], axir.arsize); `endif + if(axir.araddr[7:0]==`SR) + wr_status_read<=True; + if(axir.araddr[7:0]==`DR)begin // accessing the data register for read. + `ifdef verbose $display("Accessed DR fifo_count : %d axi.arsize: %d", fifo.count, axir.arsize); `endif + if(ccr_fmode=='b10) + wr_data_read<=True; + if(axir.arsize==0) begin // 8 bits + if(fifo.deqReadyN(1))begin + let temp=fifo.first[0]; + reg1=duplicate(temp); + fifo.deq(1); + end + end + else if(axir.arsize==1) begin // 16 bits + if(fifo.deqReadyN(2)) begin + let temp={fifo.first[0],fifo.first[1]}; + reg1=duplicate(temp); + fifo.deq(2); + end + end + else /*if(axir.arsize==2)*/ begin // 32 bits -- Even if the request is a long int, respond with int since that's the max we can do + if(fifo.deqReadyN(4)) begin + let temp={fifo.first[0],fifo.first[1],fifo.first[2],fifo.first[3]}; + reg1=duplicate(temp); + fifo.deq(4); + end + end + end + `ifdef verbose $display("Sending Response : reg1: %x", reg1); `endif + let r = AXI4_Lite_Rd_Data {rresp: AXI4_LITE_OKAY, rdata: duplicate(reg1) ,ruser: 0}; + request_ready = True; + s_xactor.i_rd_data.enq(r); + end + rg_request_ready <= request_ready; + `ifdef verbose $display($time,"QSPI: Is Request ready? : %h",request_ready); `endif + endrule + + + rule timeout_counter; + if(cr_tcen==1 && sr_tof==0) // timecounter is enabled + if(timecounter==lptr_timeout[15:0])begin + timecounter<=0; + sr_tof<=1; + end + else + timecounter<=timecounter+1; + endrule + + rule delayed_sr_tcf_signal(transfer_cond && + ((ccr_ddrm==1 && ddr_clock && (ccr_admode!=0 || ccr_dmode!=0)) || wr_sdr_clock)); + sr_tcf<=delay_sr_tcf; + endrule + + rule delayed_ncs_generation; + delay_ncs<=ncs; + endrule + + rule delay_sdr; + wr_sdr_delayed <= wr_sdr_clock; + endrule + + + /* This rule generates the clk signal. The Prescaler register defines the + division factor wrt to the Global clock. The prescaler will only work when the + chip select is low i.e when the operation has been initiated. */ + rule rl_generate_clk_from_master; + if(delay_ncs==1)begin + rg_clk_counter<=0; + rg_clk<=dcr_ckmode; + `ifdef verbose1 $display("dcr_ckmode: %h",dcr_ckmode); `endif + end + else begin + let half_clock_value=cr_prescaler>>1; + if(cr_prescaler[0]==0)begin // odd division + if(rg_clk_counter<=half_clock_value) + rg_clk<=0; + else + rg_clk<=1; + if(rg_clk_counter==cr_prescaler) + rg_clk_counter<=0; + else + rg_clk_counter<=rg_clk_counter+1; + if(rg_clk_counter == half_clock_value || rg_clk_counter==cr_prescaler)begin + wr_sdr_clock<=rg_phase==DataRead_phase?unpack(~rg_clk):unpack(rg_clk); + end + end + else begin // even division + if(rg_clk_counter==half_clock_value)begin + rg_clk<=~rg_clk; + rg_clk_counter<=0; + wr_sdr_clock<=rg_phase==DataRead_phase?unpack(~rg_clk):unpack(rg_clk); + end + else if(delay_ncs==0) + rg_clk_counter<=rg_clk_counter+1; + end + end + endrule + + /* update the status flag on each cycle */ + rule rl_update_fifo_level; + sr_flevel<=pack(fifo.count); + endrule + /* set the fifo threshold flag when the FIFO level is equal to the FTHRESH value */ + (*preempts="rl_set_busy_signal,rl_update_threshold_flag"*) + rule rl_update_threshold_flag; + if(ccr_fmode=='b00)begin// indirect write mode + sr_ftf<=pack(16-pack(fifo.count)>={1'b0,cr_fthres}+1); + end + else if(ccr_fmode=='b01) begin + sr_ftf<=pack(pack(fifo.count)>=({1'b0,cr_fthres}+1)); + `ifdef verbose1 $display("fifo count: %d fthres: %d",fifo.count,cr_fthres); `endif + end + else if(ccr_fmode=='b10 && wr_status_read)begin // auto_status polling mode + sr_ftf<=1; + end + else if(ccr_fmode=='b10 && wr_data_read)begin // auto_status polling mode + sr_ftf<=0; + end + else if(ccr_fmode=='b11 && pack(fifo.count)>={1'b0,cr_fthres}+1) begin + ncs<=1; + sr_busy<=0; + rg_phase<=Idle; // Will this work? + thres<= True; + rg_request_ready <= True; + // $display($time,"THRES is being set to TRUE kyaaaa?"); + end + endrule + + /* If abort is raised or the QSPI is disabled go back to Idle Phase*/ + //(*descending_urgency = "if_abort,rl_read_request_from_AXI"*) + //(*descending_urgency = "if_abort,rl_write_request_from_AXI"*) + (*preempts = "if_abort,rl_update_threshold_flag"*) + rule if_abort(qspi_flush); + //$display("Received Abort or Disable request, going to idle"); + rg_phase<=Idle; + ncs <= 1; + sr_busy <= 0; + thres <= False; + read_true <= False; + first_read <= False; + instruction_sent <= False; + half_cycle_delay <= False; + fifo.clear(); //What if its already empty? clearing the fifo, so doesn't matter + endrule + + /*operate the busy signal in different mode */ + rule rl_reset_busy_signal(sr_busy==1); + if(cr_abort==1)begin + sr_busy<=0; + ncs<=1; + end + else if(ccr_fmode=='b00 || ccr_fmode=='b01)begin // indirect write or read mode; + if(/*fifo.count==0 &&*/ sr_tcf==1)begin // if FIFO is empty and the transaction is complete + sr_busy<=0; + ncs<=1; + end + end + else if(ccr_fmode=='b10)begin // automatic polling mode + if(sr_smf==1)begin + sr_busy<=0; + ncs<=1; + end + end + else if(ccr_fmode=='b11)begin + if(sr_tof==1 || cr_en==0 || cr_abort==1) begin// timeout event + sr_busy<=0; + ncs<=1; + end + end + endrule + (*descending_urgency="rl_set_busy_signal,rl_read_request_from_AXI"*) + (*descending_urgency="rl_set_busy_signal,rl_write_request_from_AXI"*) + rule rl_set_busy_signal(sr_busy==0 && rg_phase==Idle && cr_abort==0 && cr_en==1); + rg_output_en<=0; + instruction_sent<=False; + `ifdef verbose1 $display($time,"\tWaiting for change in phase wr_read_request_from_AXI: %b ccr_fmode: %h thres: %h",wr_read_request_from_AXI,ccr_fmode,thres); `endif + if(wr_instruction_written)begin + sr_busy<=1; + ncs<=0; + rg_phase<=Instruction_phase; + rg_count_bits<=8; + end + else if((wr_address_written && ccr_admode!=0 && (ccr_fmode=='b01 || ccr_dmode=='d0 || ccr_fmode=='b10))|| (wr_data_written && ccr_admode!=0 && ccr_dmode!=0 && ccr_fmode=='b00))begin + sr_busy<=1; // start some transaction + `ifdef verbose $display("Address Written and going to Some mode"); `endif + ncs<=0; + let {x,y,z}<-change_phase.func(rg_phase,0,0); + rg_count_bits<=x; + rg_count_bytes<=0; + rg_phase<=z; + `ifdef verbose $display("Mode is :",fshow(z),"Count_bits : %d",x); `endif + if(z==DataRead_phase) + read_true <= True; + end + else if(wr_read_request_from_AXI && ccr_fmode=='b11 && !thres)begin // memory-mapped mode. + `ifdef verbose $display("Entering Memory mapped mode"); `endif + sr_busy<=1; + ncs<=0; + let {x,y,z}<-change_phase.func(rg_phase,0,0); + rg_count_bits<=x; + rg_count_bytes<=0; + rg_phase<=z; + `ifdef verbose $display("rg_phase :",fshow(z)); `endif + if(z==DataRead_phase) + read_true <= True; + end + endrule + + /* This rule generates the error signal interrupt in different scenarios */ + rule set_error_signal; + Bit#(32) actual_address=1<<(dcr_fsize); + if(wr_address_written && ar>actual_address && (ccr_fmode=='b00 || ccr_fmode=='b01)) + sr_tef<=1; + else if(wr_address_written && ar+dlr>actual_address &&(ccr_fmode=='b00 || ccr_fmode=='b01)) + sr_tef<=1; + else if(wr_address_written) + sr_tef<=0; + endrule + + /* Rule to transfer the instruction of 8-bits outside. THe size of instruction is fixed + to 8 bits by protocol. Instruction phase will always be in SDR mode */ + rule rl_transfer_instruction(rg_phase==Instruction_phase && transfer_cond && wr_sdr_clock && !qspi_flush); + Bool end_of_phase=False; + let reverse_instruction=ccr_instruction; + let count_val=rg_count_bits; + `ifdef verbose1 $display("Executing Instruction Phase SPI Mode: %b Count_bits: %d InstructionReverse: %h",ccr_imode,rg_count_bits,reverse_instruction); `endif + Bit#(4) enable_o=0; + if(ccr_imode=='b01)begin // single spi mode; + enable_o=4'b1101; + rg_output<={1'b1,1'b0,1'b0,reverse_instruction[rg_count_bits-1]}; + if(rg_count_bits==1)begin// end of instruction stream + end_of_phase=True; + end + else + count_val=rg_count_bits-1; + end + else if (ccr_imode=='b10)begin // dual mode; + enable_o=4'b1111; + rg_output<={1'b1,1'b0,reverse_instruction[rg_count_bits-1:rg_count_bits-2]}; + if(rg_count_bits==2)begin// end of instruction stream + end_of_phase=True; + end + else + count_val=rg_count_bits-2; + end + else if (ccr_imode=='b11)begin // quad mode; + enable_o=4'b1111; + rg_output<=reverse_instruction[rg_count_bits-1:rg_count_bits-4]; + if(rg_count_bits==4)begin// end of instruction stream + end_of_phase=True; + end + else + count_val=rg_count_bits-4; + end + if(end_of_phase || ccr_imode==0)begin // end of instruction or no instruction phase + let {x,y,z}<-change_phase.func(rg_phase,count_val,0); + instruction_sent<=True; + rg_count_bits<=x; + delay_sr_tcf<=y; + rg_phase<=z; + rg_count_bytes<=0; + if(ccr_ddrm==1) + half_cycle_delay<=True; + if(z==DataRead_phase) + read_true <= True; + end + else + rg_count_bits<=count_val; + rg_output_en<=enable_o; + endrule + + /* Rule to transfer the address bits of address outside. The size of address is + defined by the ccr_adsize register in ccr */ + rule rl_transfer_address(rg_phase==Address_phase && transfer_cond && clock_cond && !qspi_flush); + if(half_cycle_delay) begin + half_cycle_delay<=False; + read_true <= True; //A workaround for the delay .. For DDR mode, the clock should be pushed one cycle and not half + end + else if(read_true) + read_true <= False; + else begin + Bool end_of_phase=False; + Bit#(4) enable_o=0; + let count_val=rg_count_bits; + Bit#(32) address=(ccr_fmode=='b11)?zeroExtend(mm_address):ar; + rg_prev_addr <= truncate(address); + `ifdef verbose1 $display($time,"Executing Address Phase SPI Mode: %b Address Size: %d Count_bits: %d Address: %b",ccr_admode,ccr_adsize,rg_count_bits,address); `endif + if(ccr_admode=='b01)begin // single spi mode; + enable_o=4'b1101; + rg_output<={1'b1,1'b0,1'b0,address[rg_count_bits-1]}; + `ifdef verbose $display($time,"Single: Sending Address bit %h bit_number: %d total_address: %h",rg_count_bits-1,address[rg_count_bits-1],address); `endif + if(rg_count_bits==1)begin// end of address stream + end_of_phase=True; + end + else + count_val=rg_count_bits-1; + end + else if (ccr_admode=='b10)begin // dual mode; + enable_o=4'b1111; + rg_output<={1'b1,1'b0,address[rg_count_bits-1:rg_count_bits-2]}; + `ifdef verbose $display($time,"Double: Sending Address bit %h bit_number: %d total_address: %h",rg_count_bits-1,address[rg_count_bits-1],address); `endif + if(rg_count_bits==2)begin// end of address stream + end_of_phase=True; + end + else + count_val=rg_count_bits-2; + end + else if (ccr_admode=='b11)begin // quad mode; + enable_o=4'b1111; + rg_output<=address[rg_count_bits-1:rg_count_bits-4]; + `ifdef verbose $display($time,"Quad: Sending Address bit %h bit_number: %d total_address: %h",rg_count_bits-1,address[rg_count_bits-1],address); `endif + if(rg_count_bits==4)begin// end of address stream + end_of_phase=True; + end + else + count_val=rg_count_bits-4; + end + if(end_of_phase || ccr_admode==0)begin // end of address phase + let {x,y,z}<-change_phase.func(rg_phase,count_val,0); + rg_count_bits<=x; + delay_sr_tcf<=y; + rg_phase<=z; + rg_count_bytes<=0; + if(z==DataRead_phase) + read_true <= True; + end + else + rg_count_bits<=count_val; + rg_output_en<=enable_o; + end + endrule + + /* Rule to transfer the alternate bytes. The size of alternate bytes is + defined by the ccr_absize register in ccr */ + rule rl_transfer_alternatebytes(rg_phase==AlternateByte_phase && transfer_cond && clock_cond && !qspi_flush); + Bool end_of_phase=False; + let count_val=rg_count_bits; + `ifdef verbose1 $display("Executing AltByte Phase SPI Mode: %b AltByte Size: %d Count_bits: %d AltByte: %b",ccr_abmode,ccr_absize,rg_count_bits,abr); `endif + Bit#(4) enable_o=0; + if(ccr_abmode=='b01)begin // single spi mode; + enable_o=4'b1101; + rg_output<={1'b1,1'b0,1'b0,abr[rg_count_bits-1]}; + if(rg_count_bits==1)begin// end of instruction stream + end_of_phase=True; + end + else + count_val=rg_count_bits-1; + end + else if (ccr_abmode=='b10)begin // dual mode; + enable_o=4'b1111; + rg_output<={1'b1,1'b0,abr[rg_count_bits-1:rg_count_bits-2]}; + if(rg_count_bits==2)begin// end of instruction stream + end_of_phase=True; + end + else + count_val=rg_count_bits-2; + end + else if (ccr_abmode=='b11)begin // quad mode; + enable_o=4'b1111; + rg_output<=abr[rg_count_bits-1:rg_count_bits-4]; + if(rg_count_bits==4)begin// end of instruction stream + end_of_phase=True; + end + else + count_val=rg_count_bits-4; + end + if(end_of_phase || ccr_abmode==0)begin // end of alternate byte phase + let {x,y,z}<-change_phase.func(rg_phase,count_val,0); + rg_count_bits<=x; + delay_sr_tcf<=y; + rg_phase<=z; + rg_count_bytes<=0; + if(z==DataRead_phase) + read_true <= True;end + else + rg_count_bits<=count_val; + rg_output_en<=enable_o; + endrule + + + rule rl_transfer_dummy_cycle(rg_phase==Dummy_phase && transfer_cond && wr_sdr_clock && !qspi_flush); + let {x,y,z} <- change_phase.func(rg_phase,rg_count_bits,0); + Bit#(5) count_val = rg_mode_byte_counter; + Bit#(4) enable_o = rg_output_en; + `ifdef verbose $display("\t Executing Dummy Phase: rg_mode_bytes: %b rg_mode_byte_counter: %d",rg_mode_bytes, rg_mode_byte_counter); `endif + if(ccr_dmode==1) begin + if(ccr_dummy_confirmation==1) begin + //rg_output_en <= 4'b1101; + enable_o = 4'b1101; + rg_output <= {1'b1,1'b0,1'b0,rg_mode_bytes[rg_mode_byte_counter]}; + if(count_val!=0) + count_val = count_val - 1; + else + enable_o = 4'b0000; + end + else begin + //rg_output_en <= 4'b1101; + enable_o = 4'b1101; + rg_output <= {1'b1,1'b0,1'b0,1'b0}; + end + end + else if(ccr_dmode==2) begin + if(ccr_dummy_confirmation==1) begin + //rg_output_en <= 4'b1111; + enable_o = 4'b1111; + rg_output <= {1'b1,1'b0,rg_mode_bytes[rg_mode_byte_counter:rg_mode_byte_counter-1]}; + if(count_val!=0) + count_val = count_val - 2; + else + enable_o = 4'b0000; + end + else begin + //rg_output_en <= 4'b1100; + enable_o = 4'b1100; + rg_output <= {1'b1,1'b0,1'b0,1'b0}; + end + end + else begin + if(ccr_dummy_confirmation==1) begin + //rg_output_en <= 4'b1111; + enable_o = 4'b1111; + rg_output <= rg_mode_bytes[rg_mode_byte_counter:rg_mode_byte_counter-3]; + if(count_val!=3) + count_val = count_val - 4; + else + enable_o = 4'b0000; + end + else begin + //rg_output_en <= 4'b0000; + enable_o = 4'b0000; + end + end + if(rg_count_bits==0 || (rg_count_bits==1 && z!=DataRead_phase))begin // end of dummy cycles; + delay_sr_tcf<=y; + rg_phase<=z; + `ifdef verbose $display("From Dummy to :",fshow(z)); `endif + if(z==DataRead_phase) + read_true <= True; + rg_count_bytes<=0; + rg_count_bits<=x; + rg_mode_byte_counter <= 'd-1; //All ones + if(ccr_ddrm==1) + half_cycle_delay<=True; + end + else begin + rg_count_bits<=rg_count_bits-1; + rg_mode_byte_counter <= count_val; + rg_output_en <= enable_o; + end + endrule + + + /* Rule to transfer the dummy_cycles. The size of dummy cycles is + defined by the ccr_dcyc register in ccr. The number of dummy cycles should be calculated of + the complete cycle even in DDR mode hence using sdr clock*/ +/* rule rl_transfer_dummy_cycle(rg_phase==Dummy_phase && transfer_cond && wr_sdr_clock && !qspi_flush); + let {x,y,z}<-change_phase.func(rg_phase,rg_count_bits,0); + `ifdef verbose $display($time,"Executing Dummy Phase, Dummy_confirmation_bit : %d dummy_bit : %d", ccr_dummy_confirmation, ccr_dummy_bit); `endif + if(ccr_dmode==1) begin + if(ccr_dummy_confirmation==1) begin + rg_output_en <= 4'b1101; + rg_output <= {1'b1,1'b0,1'b0,ccr_dummy_bit}; + ccr_dummy_confirmation<=0; + end + else begin + rg_output_en <= 4'b1101; + rg_output <= {1'b1,1'b0,1'b0,1'b0}; + end + end + else if(ccr_dmode==2) begin + if(ccr_dummy_confirmation==1) begin + rg_output_en <= 4'b1101; + rg_output <= {1'b1,1'b0,1'b0,ccr_dummy_bit}; + ccr_dummy_confirmation <= 0; + end + else begin + rg_output_en <= 4'b1100; + rg_output <= {1'b1,1'b0,1'b0,1'b0}; + end + end + else begin + if(ccr_dummy_confirmation==1) begin + `ifdef verbose $display("Data going to output %d", ccr_dummy_bit); `endif + rg_output_en <= 1; + rg_output[0] <= ccr_dummy_bit; + ccr_dummy_confirmation<=0; + end + else + rg_output_en <= 0; + end + if(rg_count_bits==0 || (rg_count_bits==1 && z!=DataRead_phase))begin // end of dummy cycles; + delay_sr_tcf<=y; + rg_phase<=z; + `ifdef verbose $display("From Dummy to :",fshow(z)); `endif + if(z==DataRead_phase) + read_true <= True; + rg_count_bytes<=0; + rg_count_bits<=x; + if(ccr_ddrm==1) + half_cycle_delay<=True; + end + else begin + rg_count_bits<=rg_count_bits-1; + end + endrule*/ + + /* read data from the flash memory and store it in the DLR register. Simulataneously + put Bytes in the FIFO*/ + (*descending_urgency="rl_data_read_phase,rl_read_request_from_AXI"*) + (*descending_urgency="rl_data_read_phase,rl_write_request_from_AXI"*) + rule rl_data_read_phase(rg_phase==DataRead_phase /*&& ccr_fmode!='b11*/ && transfer_cond && clock_cond && !qspi_flush); + //rg_output_en<=0; + if(half_cycle_delay || read_true) begin + half_cycle_delay<=False; + read_true <= False; + end + else begin + Bit#(32) data_reg=dr; + Bit#(32) count_byte=rg_count_bytes; + Bit#(32) count_bits=rg_count_bits; + Bit#(32) data_length1=(ccr_fmode=='b11)?mm_data_length:dlr; + `ifdef verbose1 $display($time,"Executing DataRead Phase SPI Mode: %b DLR : %d Count_bits: %d Input :%b ccr_ddrm: %b",ccr_dmode,data_length1,rg_count_bits,rg_input,ccr_ddrm); `endif + /* write incoming bit to the data register */ + if(ccr_dmode==1)begin // single line mode; + data_reg=data_reg<<1; + data_reg[0]=rg_input[1]; + `ifdef verbose $display($time,"Single data_reg : %b",data_reg); `endif + count_bits=count_bits+1; + rg_output_en <= 4'b1101; + rg_output <= {1'b1,1'b0,1'b0,1'b0}; + + end + else if(ccr_dmode==2)begin // dual line mode; + rg_output_en <= 4'b1100; + data_reg=data_reg<<2; + data_reg[1:0]=rg_input[1:0]; + `ifdef verbose $display($time,"Dual data_reg : %b",data_reg); `endif + count_bits=count_bits+1; + rg_output <= {1'b1,1'b0,1'b0,1'b0}; + end + else if(ccr_dmode==3) begin// quad line mode; + rg_output_en <= 4'b0000; + data_reg=data_reg<<4; + data_reg[3:0]=rg_input; + `ifdef verbose $display($time,"Quad data_reg : %b",data_reg); `endif + count_bits=count_bits+1; + end + + /* write the last successfully received byte into the FIFO */ + if(ccr_dmode==1)begin// single line mode + if(count_byte==data_length-1 && ccr_ddrm==1 && count_bits[2:0]=='b111) //To make sure that the Flash does not send any data the next half edge since ncs is made 1 after the second edge + ncs<=1; + if(rg_count_bits[2:0]=='b111)begin // multiple of eight bits have been read. + `ifdef verbose1 $display("Enquing FIFO"); `endif + Vector#(4,Bit#(8)) temp = newVector(); + temp[0]=data_reg[7:0]; + `ifdef verbose $display($time,"Single Enqueing FIFO : data is %h",temp[0]); `endif + if(!first_read) + fifo.enq(1,temp); + count_byte=count_byte+1; + end + end + else if(ccr_dmode==2) begin // dual line mode + if(count_byte==data_length-1 && ccr_ddrm==1 && count_bits[1:0]=='b11) //To make sure that the Flash does not send any data the next half edge since ncs is made 1 after the second edge + ncs<=1; + if(rg_count_bits[1:0]=='b11)begin // multiple of eight bits have been read. + `ifdef verbose1 $display("Enquing FIFO"); `endif + Vector#(4,Bit#(8)) temp = newVector(); + temp[0]=data_reg[7:0]; + `ifdef verbose $display($time,"Dual Enqueing FIFO : data is %h",temp[0]); `endif + if(!first_read) + fifo.enq(1,temp); + count_byte=count_byte+1; + end + end + else if(ccr_dmode==3) begin // quad line mode + if(count_byte==data_length-1 && ccr_ddrm==1 && count_bits[0]=='b1) //To make sure that the Flash does not send any data the next half edge since ncs is made 1 after the second edge + ncs<=1; + if(rg_count_bits[0]=='b1)begin // multiple of eight bits have been read. + `ifdef verbose1 $display("Enquing FIFO"); `endif + Vector#(4,Bit#(8)) temp = newVector(); + temp[0]=data_reg[7:0]; + `ifdef verbose $display($time,"Quad Enqueing FIFO : data is %h",temp[0]); `endif + if(!first_read) + fifo.enq(1,temp); + count_byte=count_byte+1; + end + end + + bit smf=0; + `ifdef verbose $display("count_byte: %d data_length1: %d",count_byte,data_length1); `endif + /* condition for termination of dataread_phase */ + if(data_length1!='hFFFFFFFF)begin // if limit is not undefined + if(count_byte==data_length1)begin // if limit has bee reached. + `ifdef verbose $display($time,"Limit has reached: rg_count_bytes %h data_length %h",count_byte,data_length); `endif + if(ccr_fmode=='b10)begin // auto-status polling mode + if(cr_pmm==0)begin // ANDed mode + if((psmar&psmkr) == (psmkr&dr)) // is the unmasked bits match + smf=1; + else + smf=0; + end + else begin// ORed mode + let p=psmkr&dr; + let q=psmkr&psmar; + let r=~(p^q); + if(|(r)==1) + smf=1; + else + smf=0; + end + end + else if(ccr_fmode=='b11)begin// memory mapped mode + if(first_read) begin + `ifdef verbose $display("Sending response back to the proc data_reg: %h",data_reg); `endif + let r = AXI4_Lite_Rd_Data {rresp: AXI4_LITE_OKAY, rdata: duplicate(data_reg) , ruser: 0}; + s_xactor.i_rd_data.enq(r); + first_read <= False; + //rg_request_ready <= True; + end + end + let {x,y,z}<-change_phase.func(rg_phase,rg_count_bits,smf); + /* if(z==DataRead_phase) + read_true <= True;*/ + rg_phase<=z; + `ifdef verbose $display("rg_phase:",fshow(z),"sr_tcf: %d",y); `endif + sr_tcf<=y; // set completion of transfer flag + rg_count_bytes<=0; + rg_count_bits<=0; + end + else begin + rg_count_bytes<=count_byte; + rg_count_bits<=count_bits; + end + end + else if(dcr_fsize!='h1f)begin // if limit is not infinite + Bit#(32) new_limit=1<<(dcr_fsize); + `ifdef verbose1 $display("Sending completion -- newlimit : %h",new_limit); `endif + if(truncate(rg_count_bytes)==new_limit)begin // if reached end of Flash memory + let {x,y,z}<-change_phase.func(rg_phase,rg_count_bits,smf&cr_apms); + rg_phase<=z; + if(z==DataRead_phase) + read_true <= True; + sr_tcf<=y; // set completion of transfer flag + rg_count_bytes<=0; + rg_count_bits<=0; + end + else begin + rg_count_bytes<=count_byte; + rg_count_bits<=count_bits; + end + end + else begin // keep looping untill abort signal is not raised. + rg_count_bytes<=count_byte; + rg_count_bits<=count_bits; + end + dr<=data_reg; + sr_smf<=smf; + end + endrule + + /* write data from the FIFO to the FLASH. Simulataneously*/ + (*descending_urgency="rl_data_write_phase,rl_read_request_from_AXI"*) + (*descending_urgency="rl_data_write_phase,rl_write_request_from_AXI"*) + rule rl_data_write_phase(rg_phase==DataWrite_phase && transfer_cond && clock_cond && !qspi_flush); + if(half_cycle_delay) + half_cycle_delay<=False; + else begin + Bit#(8) data_reg=fifo.first()[0]; + Bit#(32) count_byte=rg_count_bytes; + Bit#(32) count_bits=rg_count_bits; + Bit#(4) enable_o=0; + /* write incoming bit to the data register */ + if(ccr_dmode==1)begin // single line mode; + enable_o=4'b1101; + rg_output<={1'b1,1'b0,1'b0,data_reg[rg_count_bits-1]}; + count_bits=count_bits-1; + end + else if(ccr_dmode==2)begin // dual line mode; + enable_o=4'b1111; + rg_output<={1'b1,1'b0,data_reg[rg_count_bits-1:rg_count_bits-2]}; + count_bits=count_bits-2; + end + else if(ccr_dmode==3) begin// quad line mode; + enable_o=4'b1111; + rg_output<=data_reg[rg_count_bits-1:rg_count_bits-4]; + count_bits=count_bits-4; + end + `ifdef verbose1 $display("Executing DataWrite Phase SPI Mode: %b DLR : %d Count_bits: %d Input :%b Enable: %b",ccr_dmode,dlr,rg_count_bits,rg_input,enable_o); `endif + + /* write the last successfully received byte into the FIFO */ + if(ccr_dmode==1)begin// single line mode + if(rg_count_bits==1)begin // multiple of eight bits have been read. + fifo.deq(1); + count_byte=count_byte+1; + count_bits=8; + end + end + else if(ccr_dmode==2) begin // dual line mode + if(rg_count_bits==2)begin // multiple of eight bits have been read. + fifo.deq(1); + count_byte=count_byte+1; + count_bits=8; + end + end + else if(ccr_dmode==3) begin // quad line mode + if(rg_count_bits==4)begin // multiple of eight bits have been read. + fifo.deq(1); + count_byte=count_byte+1; + count_bits=8; + end + end + + /* condition for termination of dataread_phase */ + if(dlr!='hFFFFFFFF)begin // if limit is not undefined + if(rg_count_bytes==dlr)begin // if limit has bee reached. + rg_phase<=Idle; + sr_tcf<=1; // set completion of transfer flag + rg_count_bytes<=0; + rg_count_bits<=0; + end + else begin + rg_count_bytes<=count_byte; + rg_count_bits<=count_bits; + end + end + else if(dcr_fsize!='h1f)begin // if limit is not infinite + Bit#(32) new_limit=1<<(dcr_fsize); + if(truncate(rg_count_bytes)==new_limit)begin // if reached end of Flash memory + rg_phase<=Idle; + sr_tcf<=1; // set completion of transfer flag + rg_count_bytes<=0; + rg_count_bits<=0; + end + else begin + rg_count_bytes<=count_byte; + rg_count_bits<=count_bits; + end + end + else begin // keep looping untill abort signal is not raised. + rg_count_bytes<=count_byte; + rg_count_bits<=count_bits; + end + rg_output_en<=enable_o; + end + endrule + + rule display_all_Registers; + `ifdef verbose1 $display($time,"\tPhase: ",fshow(rg_phase)," CR WRitten %d",wr_instruction_written, "Address Written: %d",wr_address_written); `endif + `ifdef verbose1 $display($time,"\tCR: %h\tDCR: %h\tSR: %h\tFCR: %h",cr,dcr,sr,fcr); `endif + `ifdef verbose1 $display($time,"\tDLR: %h\tCCR: %h\tAR: %h\tABR: %h",dlr,ccr,ar,abr); `endif + `ifdef verbose1 $display($time,"\tDR: %h\tPSMKR: %h\tPSMAR: %h\tPIR: %h",dr,psmkr,psmar,pir,"\n"); `endif + endrule + + `ifdef simulate + rule delay_phase(((wr_sdr_clock && ccr_ddrm==0) || (ddr_clock && ccr_ddrm==1))); + rg_phase_delayed<=rg_phase; + endrule + `endif + + interface QSPI_out out; + method bit clk_o; + return delay_ncs==1?dcr_ckmode:rg_clk; + endmethod + method Bit#(9) io0_sdio_ctrl; + return sdio0r[8:0]; + endmethod + method Bit#(9) io1_sdio_ctrl; + return sdio1r[8:0]; + endmethod + method Bit#(9) io2_sdio_ctrl; + return sdio2r[8:0]; + endmethod + method Bit#(9) io3_sdio_ctrl; + return sdio3r[8:0]; + endmethod + method Bit#(4) io_o; + return rg_output; + endmethod + method Bit#(4) io_enable; + return rg_output_en; + endmethod + method Action io_i (Bit#(4) io_in); // in + rg_input<=io_in; + endmethod + method bit ncs_o = ncs; + endinterface + + interface slave= s_xactor.axi_side; + + method Bit#(6) interrupts; // 0=TOF, 1=SMF, 2=Threshold, 3=TCF, 4=TEF 5=request_ready + return {pack(rg_request_ready),sr_tef&cr_teie, sr_tcf&cr_tcie, sr_ftf&cr_ftie, sr_smf&cr_smie , sr_tof&cr_toie}; + endmethod + `ifdef simulate method curphase = rg_phase_delayed; `endif + endmodule + +endpackage diff --git a/src/peripherals/qspi/qspi.defs b/src/peripherals/qspi/qspi.defs new file mode 100644 index 0000000..11738c5 --- /dev/null +++ b/src/peripherals/qspi/qspi.defs @@ -0,0 +1,19 @@ +`define CR 'h00 +`define DCR 'h04 +`define SR 'h08 +`define FCR 'h0c +`define DLR 'h10 +`define CCR 'h14 +`define AR 'h18 +`define ABR 'h1c +`define DR 'h20 +`define PSMKR 'h24 +`define PSMAR 'h28 +`define PIR 'h2c +`define LPTR 'h30 +`define SDIO0 'h34 +`define SDIO1 'h38 +`define SDIO2 'h3c +`define SDIO3 'h40 +`define STARTMM 'h0000000 +`define ENDMM 'hFFFFFFF diff --git a/src/peripherals/sdmmc/sdcard_dummy.bsv b/src/peripherals/sdmmc/sdcard_dummy.bsv new file mode 100644 index 0000000..bc12be4 --- /dev/null +++ b/src/peripherals/sdmmc/sdcard_dummy.bsv @@ -0,0 +1,97 @@ +/* +Copyright (c) 2013, IIT Madras All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted +provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions + and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of + conditions and the following disclaimer in the documentation and/or other materials provided + with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or + promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY +AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT +OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +-------------------------------------------------------------------------------------------------- + +Author: Neel Gala +Email id: neelgala@gmail.com +Details: + +-------------------------------------------------------------------------------------------------- +*/ +package sdcard_dummy; + `include "instance_defines.bsv" + import ClockDiv::*; + import ConcatReg::*; + import Semi_FIFOF::*; + import BUtils ::*; + import AXI4_Lite_Types::*; + + interface Ifc_sdcard_dummy; + interface AXI4_Lite_Slave_IFC#(`ADDR, `DATA, `USERSPACE) slave; + method Bit#(1) cmd; + method Bit#(1) clk; + method Bit#(1) d0_out; + method Bit#(1) d0_outen; + method Action d0_in(Bit#(1) in); + method Bit#(1) d1_out; + method Bit#(1) d1_outen; + method Action d1_in(Bit#(1) in); + method Bit#(1) d2_out; + method Bit#(1) d2_outen; + method Action d2_in(Bit#(1) in); + method Bit#(1) d3_out; + method Bit#(1) d3_outen; + method Action d3_in(Bit#(1) in); + endinterface + (*synthesize*) + module mksdcard_dummy(Ifc_sdcard_dummy); + AXI4_Lite_Slave_Xactor_IFC#(`ADDR,`DATA, `USERSPACE) s_xactor<-mkAXI4_Lite_Slave_Xactor(); + Reg#(Bit#(1)) rg_cmd <- mkReg(0); + Reg#(Bit#(1)) rg_clk <- mkReg(0); + Reg#(Bit#(1)) rg_d0_out <- mkReg(0); + Reg#(Bit#(1)) rg_d0_outen <- mkReg(0); + Reg#(Bit#(1)) rg_d0_in <- mkReg(0); + Reg#(Bit#(1)) rg_d1_out <- mkReg(0); + Reg#(Bit#(1)) rg_d1_outen <- mkReg(0); + Reg#(Bit#(1)) rg_d1_in <- mkReg(0); + Reg#(Bit#(1)) rg_d2_out <- mkReg(0); + Reg#(Bit#(1)) rg_d2_outen <- mkReg(0); + Reg#(Bit#(1)) rg_d2_in <- mkReg(0); + Reg#(Bit#(1)) rg_d3_out <- mkReg(0); + Reg#(Bit#(1)) rg_d3_outen <- mkReg(0); + Reg#(Bit#(1)) rg_d3_in <- mkReg(0); + method cmd = rg_cmd; + method clk = rg_clk; + method d0_out=rg_d0_out; + method d0_outen=rg_d0_outen; + method Action d0_in(Bit#(1) in); + rg_d0_in<= in; + endmethod + method d1_out=rg_d1_out; + method d1_outen=rg_d1_outen; + method Action d1_in(Bit#(1) in); + rg_d1_in<= in; + endmethod + method d2_out=rg_d2_out; + method d2_outen=rg_d2_outen; + method Action d2_in(Bit#(1) in); + rg_d2_in<= in; + endmethod + method d3_out=rg_d3_out; + method d3_outen=rg_d3_outen; + method Action d3_in(Bit#(1) in); + rg_d3_in<= in; + endmethod + interface slave=s_xactor.axi_side; + endmodule +endpackage diff --git a/src/peripherals/uart/RS232_modified.bsv b/src/peripherals/uart/RS232_modified.bsv new file mode 100644 index 0000000..ca336d9 --- /dev/null +++ b/src/peripherals/uart/RS232_modified.bsv @@ -0,0 +1,747 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ +//////////////////////////////////////////////////////////////////////////////// +// Copyright (c) 2010 Bluespec, Inc. ALL RIGHTS RESERVED. +//////////////////////////////////////////////////////////////////////////////// +// Filename : RS232.bsv +// Description : Simple UART BFM RS232 <-> Bit#(8) +//////////////////////////////////////////////////////////////////////////////// +package RS232_modified; + +// Notes : + +//////////////////////////////////////////////////////////////////////////////// +/// Imports +//////////////////////////////////////////////////////////////////////////////// +import Clocks ::*; +import GetPut ::*; +import Connectable ::*; +import FIFOLevel ::*; +import Vector ::*; +import BUtils ::*; +import Counter ::*; + +//////////////////////////////////////////////////////////////////////////////// +/// Exports +//////////////////////////////////////////////////////////////////////////////// +export RS232 (..); +export UART (..); +export BaudGenerator (..); +export Parity (..); +export StopBits (..); +export InputFilter (..); +export Synchronizer (..); +export EdgeDetector (..); +export InputMovingFilter (..); +export mkUART; +export mkBaudGenerator; +export mkInputFilter; +export mkSynchronizer; +export mkEdgeDetector; +export mkInputMovingFilter; + +//////////////////////////////////////////////////////////////////////////////// +/// Types +//////////////////////////////////////////////////////////////////////////////// +typedef union tagged { + void Start; + void Center; + void Wait; + void Sample; + void Parity; + void StopFirst; + void StopLast; + } RecvState deriving (Bits, Eq); + +typedef union tagged { + void Idle; + void Start; + void Wait; + void Shift; + void Stop; + void Stop5; + void Stop2; + void Parity; + } XmitState deriving (Bits, Eq); + +typedef enum { + NONE, + ODD, + EVEN + } Parity deriving (Bits, Eq); + +typedef enum { + STOP_1, + STOP_1_5, + STOP_2 + } StopBits deriving (Bits, Eq); + +//////////////////////////////////////////////////////////////////////////////// +/// Interfaces +//////////////////////////////////////////////////////////////////////////////// +(* always_ready, always_enabled *) +interface RS232; + // Inputs + (* prefix = "" *) + method Action sin((* port = "SIN" *)Bit#(1) x); + // Outputs + (* prefix = "", result = "SOUT" *) + method Bit#(1) sout(); +endinterface + +interface BaudGenerator; + method Action clock_enable(); + method Action clear(); + method Bool baud_tick_16x(); + method Bool baud_tick_2x(); +endinterface + +interface InputFilter#(numeric type size, type a); + method Action clock_enable(); + method a _read(); +endinterface + +(* always_ready, always_enabled *) +interface EdgeDetector#(type a); + method Bool rising(); + method Bool falling(); +endinterface + +(* always_ready, always_enabled *) +interface Synchronizer#(type a); + method Action _write(a x); + method a _read(); +endinterface + +interface InputMovingFilter#(numeric type width, numeric type threshold, type a); + method Action sample(); + method Action clear(); + method a _read(); +endinterface + +interface UART#(numeric type depth); + (* prefix = "" *) + interface RS232 rs232; + interface Get#(Bit#(8)) tx; + interface Put#(Bit#(8)) rx; + method Bool transmission_done; + method Bool receiver_not_empty; + method Bool receiver_not_full; + method Bool transmittor_not_empty; +endinterface + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// +/// Implementation of Baud Generator +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +module mkBaudGenerator#(Bit#(16) divider)(BaudGenerator); + + //////////////////////////////////////////////////////////////////////////////// + /// Design Elements + //////////////////////////////////////////////////////////////////////////////// + Counter#(16) rBaudCounter <- mkCounter(0); + PulseWire pwBaudTick16x <- mkPulseWire; + + Counter#(3) rBaudTickCounter <- mkCounter(0); + PulseWire pwBaudTick2x <- mkPulseWire; + + Wire#(Bit#(16)) wBaudCount <- mkWire; + rule baud_count_wire; + wBaudCount <= rBaudCounter.value; + endrule + Wire#(Bit#(3)) wBaudTickCount <- mkWire; + rule baud_tick_count_wire; + wBaudTickCount <= rBaudTickCounter.value; + endrule + + //////////////////////////////////////////////////////////////////////////////// + /// Rules + //////////////////////////////////////////////////////////////////////////////// + rule count_baudtick_16x(pwBaudTick16x); + rBaudTickCounter.up; + endrule + + rule assert_2x_baud_tick(rBaudTickCounter.value() == 0 && pwBaudTick16x); + pwBaudTick2x.send; + endrule + + //////////////////////////////////////////////////////////////////////////////// + /// Interface Connections / Methods + //////////////////////////////////////////////////////////////////////////////// + method Action clock_enable(); + if (rBaudCounter.value() + 1 >= divider) begin + pwBaudTick16x.send; + rBaudCounter.clear; + end + else begin + rBaudCounter.up; + end + endmethod + + method Action clear(); + rBaudCounter.clear; + endmethod + + method Bool baud_tick_16x(); + return pwBaudTick16x; + endmethod + + method Bool baud_tick_2x(); + return pwBaudTick2x; + endmethod + +endmodule: mkBaudGenerator + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// +/// Implementation of Input Filter +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +module mkInputFilter#(a initval, a din)(InputFilter#(size, a)) + provisos( Bits#(a, sa) + , Eq#(a) + , Add#(0, sa, 1) + , Log#(size, logsize) + , Add#(logsize, 1, csize) + ); + + //////////////////////////////////////////////////////////////////////////////// + /// Design Elements + //////////////////////////////////////////////////////////////////////////////// + Counter#(csize) counter <- mkCounter(0); + Reg#(a) rOut <- mkReg(initval); + + //////////////////////////////////////////////////////////////////////////////// + /// Rules + //////////////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////////////// + /// Interface Connections / Methods + //////////////////////////////////////////////////////////////////////////////// + method Action clock_enable(); + if (din == unpack(1) && counter.value() != fromInteger(valueof(size))) + counter.up; + else if (din == unpack(0) && counter.value() != 0) + counter.down; + + if (counter.value() == fromInteger(valueof(size))) + rOut <= unpack(1); + else if (counter.value() == 0) + rOut <= unpack(0); + endmethod + + method a _read; + return rOut; + endmethod + +endmodule + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// +/// Implementation +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +module mkEdgeDetector#(a initval, a din)(EdgeDetector#(a)) + provisos( Bits#(a, sa) + , Eq#(a) + , Add#(0, sa, 1) + ); + + //////////////////////////////////////////////////////////////////////////////// + /// Design Elements + //////////////////////////////////////////////////////////////////////////////// + Reg#(a) rDinD1 <- mkReg(initval); + + //////////////////////////////////////////////////////////////////////////////// + /// Rules + //////////////////////////////////////////////////////////////////////////////// + (* fire_when_enabled *) + (* no_implicit_conditions *) + rule pipeline; + rDinD1 <= din; + endrule + + //////////////////////////////////////////////////////////////////////////////// + /// Interface Connections / Methods + //////////////////////////////////////////////////////////////////////////////// + method Bool rising(); + return (din == unpack(1) && rDinD1 == unpack(0)); + endmethod + + method Bool falling(); + return (din == unpack(0) && rDinD1 == unpack(1)); + endmethod + +endmodule: mkEdgeDetector + +//////////////////////////////////////////////////////////////////////////////// +/// +//////////////////////////////////////////////////////////////////////////////// +function Bool getRising(EdgeDetector#(a) ifc); + return ifc.rising; +endfunction + +function Bool getFalling(EdgeDetector#(a) ifc); + return ifc.falling; +endfunction + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// +/// Implementation of Synchronizer +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +module mkSynchronizer#(a initval)(Synchronizer#(a)) + provisos( Bits#(a, sa) + , Add#(0, sa, 1) + ); + + //////////////////////////////////////////////////////////////////////////////// + /// Design Elements + //////////////////////////////////////////////////////////////////////////////// + Reg#(a) d1 <- mkReg(initval); + Reg#(a) d2 <- mkReg(initval); + + //////////////////////////////////////////////////////////////////////////////// + /// Interface Connections / Methods + //////////////////////////////////////////////////////////////////////////////// + method Action _write(x); + d1 <= x; + d2 <= d1; + endmethod + + method a _read(); + return d2; + endmethod + +endmodule: mkSynchronizer + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// +/// Implementation of Input Filter +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +module mkInputMovingFilter#(a din)(InputMovingFilter#(width, threshold, a)) + provisos( Bits#(a, sa) + , Eq#(a) + , Add#(0, sa, 1) + ); + + //////////////////////////////////////////////////////////////////////////////// + /// Design Elements + //////////////////////////////////////////////////////////////////////////////// + Counter#(width) counter <- mkCounter(0); + Reg#(a) rOut <- mkReg(unpack(0)); + PulseWire pwSample <- mkPulseWire; + + //////////////////////////////////////////////////////////////////////////////// + /// Rules + //////////////////////////////////////////////////////////////////////////////// + (* preempts = "threshold_compare, take_sample" *) + rule threshold_compare(counter.value() >= fromInteger(valueof(threshold))); + rOut <= unpack(1); + endrule + + rule take_sample(pwSample && din == unpack(1)); + counter.up; + endrule + + //////////////////////////////////////////////////////////////////////////////// + /// Interface Connections / Methods + //////////////////////////////////////////////////////////////////////////////// + method Action sample(); + pwSample.send; + endmethod + + method Action clear(); + counter.clear(); + rOut <= unpack(0); + endmethod + + method a _read; + return rOut; + endmethod + +endmodule + + +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +/// +/// Implementation of UART +/// +//////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////// +module mkUART( Bit#(4) charsize + , Parity paritysel + , StopBits stopbits + , Bit#(16) divider + , UART#(d) ifc) + provisos(Add#(2, _1, d)); + + Integer fifodepth = valueof(d); + + //////////////////////////////////////////////////////////////////////////////// + /// Design Elements + //////////////////////////////////////////////////////////////////////////////// + let baudGen <- mkBaudGenerator( divider ); + + //////////////////////////////////////////////////////////////////////////////// + /// Receive UART + //////////////////////////////////////////////////////////////////////////////// + FIFOLevelIfc#(Bit#(8), d) fifoRecv <- mkGFIFOLevel(True, False, True); + + Vector#(8, Reg#(Bit#(1))) vrRecvBuffer <- replicateM(mkRegU); + + Reg#(Bit#(1)) rRecvData <- mkReg(1); + + Reg#(RecvState) rRecvState <- mkRegA(Start); + Reg#(Bit#(4)) rRecvCellCount <- mkRegA(0); + Reg#(Bit#(4)) rRecvBitCount <- mkRegA(0); + Reg#(Bit#(1)) rRecvParity <- mkRegA(0); + + PulseWire pwRecvShiftBuffer <- mkPulseWire; + PulseWire pwRecvCellCountReset <- mkPulseWire; + PulseWire pwRecvResetBitCount <- mkPulseWire; + PulseWire pwRecvEnableBitCount <- mkPulseWire; + + //////////////////////////////////////////////////////////////////////////////// + /// Transmit UART + //////////////////////////////////////////////////////////////////////////////// + FIFOLevelIfc#(Bit#(8), d) fifoXmit <- mkGFIFOLevel(False, False, True); + + Vector#(8, Reg#(Bit#(1))) vrXmitBuffer <- replicateM(mkRegU); + + Reg#(XmitState) rXmitState <- mkRegA(Idle); + Reg#(Bit#(4)) rXmitCellCount <- mkRegA(0); + Reg#(Bit#(4)) rXmitBitCount <- mkRegA(0); + Reg#(Bit#(1)) rXmitDataOut <- mkRegA(1); + Reg#(Bit#(1)) rXmitParity <- mkRegA(0); + + PulseWire pwXmitCellCountReset <- mkPulseWire; + PulseWire pwXmitResetBitCount <- mkPulseWire; + PulseWire pwXmitEnableBitCount <- mkPulseWire; + PulseWire pwXmitLoadBuffer <- mkPulseWire; + PulseWire pwXmitShiftBuffer <- mkPulseWire; + + //////////////////////////////////////////////////////////////////////////////// + /// Definitions + //////////////////////////////////////////////////////////////////////////////// + let tick = baudGen.baud_tick_16x; + + //////////////////////////////////////////////////////////////////////////////// + /// Baud Clock Enable + //////////////////////////////////////////////////////////////////////////////// + (* no_implicit_conditions, fire_when_enabled *) + rule baud_generator_clock_enable; + baudGen.clock_enable; + endrule + + //////////////////////////////////////////////////////////////////////////////// + /// Receive Rules + //////////////////////////////////////////////////////////////////////////////// + rule receive_bit_cell_time_counter(tick); + if (pwRecvCellCountReset) + rRecvCellCount <= 0; + else + rRecvCellCount <= rRecvCellCount + 1; + endrule + + rule receive_buffer_shift(pwRecvShiftBuffer); + let v = shiftInAtN(readVReg(vrRecvBuffer), rRecvData); + writeVReg(vrRecvBuffer, v); + endrule + + rule receive_bit_counter; + if (pwRecvResetBitCount) + rRecvBitCount <= 0; + else if (pwRecvEnableBitCount) + rRecvBitCount <= rRecvBitCount + 1; + endrule + + rule receive_wait_for_start_bit(rRecvState == Start && tick); + pwRecvCellCountReset.send(); + if (rRecvData == 1'b0) begin + rRecvState <= Center; + end + else begin + rRecvState <= Start; + pwRecvResetBitCount.send(); + end + endrule + + rule receive_find_center_of_bit_cell(rRecvState == Center && tick); + if (rRecvCellCount == 4'h4) begin + pwRecvCellCountReset.send(); + if (rRecvData == 1'b0) + rRecvState <= Wait; + else + rRecvState <= Start; + end + else begin + rRecvState <= Center; + end + endrule + + rule receive_wait_bit_cell_time_for_sample(rRecvState == Wait && rRecvCellCount == 4'hF && tick); + pwRecvCellCountReset.send; + + if (rRecvBitCount == charsize) begin + if (paritysel != NONE) + rRecvState <= Parity; + else if (stopbits != STOP_1) + rRecvState <= StopFirst; + else + rRecvState <= StopLast; + end + else if (rRecvBitCount == charsize + 1) begin + if (paritysel == NONE || stopbits == STOP_1) + rRecvState <= StopLast; + else + rRecvState <= StopFirst; + end + else if (rRecvBitCount == charsize + 2) begin + rRecvState <= StopLast; + end + else begin + rRecvState <= Sample; + end + endrule + + rule receive_sample_pin(rRecvState == Sample && tick); + pwRecvShiftBuffer.send; + pwRecvEnableBitCount.send; + pwRecvCellCountReset.send; + rRecvState <= Wait; + endrule + + rule receive_parity_bit(rRecvState == Parity && tick); + rRecvParity <= rRecvData; + pwRecvEnableBitCount.send; + pwRecvCellCountReset.send; + rRecvState <= Wait; + endrule + + rule receive_stop_first_bit(rRecvState == StopFirst && tick); + pwRecvEnableBitCount.send; + pwRecvCellCountReset.send; + if (rRecvData == 1) + rRecvState <= Wait; + else + rRecvState <= Start; + endrule + + rule receive_stop_last_bit(rRecvState == StopLast && tick); + Vector#(8, Bit#(1)) data = take(readVReg(vrRecvBuffer)); + Bit#(8) bitdata = pack(data) >> (8 - charsize); + + fifoRecv.enq(bitdata); + rRecvState <= Start; + pwRecvCellCountReset.send; + endrule + + //////////////////////////////////////////////////////////////////////////////// + /// Transmit Rules + //////////////////////////////////////////////////////////////////////////////// + rule transmit_bit_cell_time_counter(tick); + if (pwXmitCellCountReset) + rXmitCellCount <= 0; + else + rXmitCellCount <= rXmitCellCount + 1; + endrule + + rule transmit_bit_counter; + if (pwXmitResetBitCount) + rXmitBitCount <= 0; + else if (pwXmitEnableBitCount) + rXmitBitCount <= rXmitBitCount + 1; + endrule + + rule transmit_buffer_load(pwXmitLoadBuffer); + Bit#(8) data = pack(fifoXmit.first); + fifoXmit.deq; + + writeVReg(vrXmitBuffer, unpack(data)); + rXmitParity <= parity(data); + endrule + + rule transmit_buffer_shift(!pwXmitLoadBuffer && pwXmitShiftBuffer); + let v = shiftInAtN(readVReg(vrXmitBuffer), 1); + writeVReg(vrXmitBuffer, v); + endrule + + rule transmit_wait_for_start_command(rXmitState == Idle && tick); + rXmitDataOut <= 1'b1; + pwXmitResetBitCount.send; + if (fifoXmit.notEmpty) begin + pwXmitCellCountReset.send; + pwXmitLoadBuffer.send; + rXmitState <= Start; + end + else begin + rXmitState <= Idle; + end + endrule + + rule transmit_send_start_bit(rXmitState == Start && tick); + rXmitDataOut <= 1'b0; + if (rXmitCellCount == 4'hF) begin + rXmitState <= Wait; + pwXmitCellCountReset.send; + end + else begin + rXmitState <= Start; + end + endrule + + rule transmit_wait_1_bit_cell_time(rXmitState == Wait && tick); + rXmitDataOut <= head(readVReg(vrXmitBuffer)); + if (rXmitCellCount == 4'hF) begin + pwXmitCellCountReset.send; + if (rXmitBitCount == (charsize - 1) && (paritysel == NONE)) begin + rXmitState <= Stop; + end + else if (rXmitBitCount == (charsize - 1) && (paritysel != NONE)) begin + rXmitState <= Parity; + end + else begin + rXmitState <= Shift; + pwXmitEnableBitCount.send; + end + end + else begin + rXmitState <= Wait; + end + endrule + + rule transmit_shift_next_bit(rXmitState == Shift && tick); + rXmitDataOut <= head(readVReg(vrXmitBuffer)); + rXmitState <= Wait; + pwXmitShiftBuffer.send; + endrule + + rule transmit_send_parity_bit(rXmitState == Parity && tick); + case(paritysel) matches + ODD: rXmitDataOut <= rXmitParity; + EVEN: rXmitDataOut <= ~rXmitParity; + default: rXmitDataOut <= 1'b0; + endcase + + if (rXmitCellCount == 4'hF) begin + rXmitState <= Stop; + pwXmitCellCountReset.send; + end + else begin + rXmitState <= Parity; + end + endrule + + rule transmit_send_stop_bit(rXmitState == Stop && tick); + rXmitDataOut <= 1'b1; + if (rXmitCellCount == 4'hF && (stopbits == STOP_1)) begin + rXmitState <= Idle; + pwXmitCellCountReset.send; + end + else if (rXmitCellCount == 4'hF && (stopbits == STOP_2)) begin + rXmitState <= Stop2; + pwXmitCellCountReset.send; + end + else if (rXmitCellCount == 4'hF && (stopbits == STOP_1_5)) begin + rXmitState <= Stop5; + pwXmitCellCountReset.send; + end + else begin + rXmitState <= Stop; + end + endrule + + rule transmit_send_stop_bit1_5(rXmitState == Stop5 && tick); + rXmitDataOut <= 1'b1; + if (rXmitCellCount == 4'h7) begin + rXmitState <= Idle; + pwXmitCellCountReset.send; + end + else begin + rXmitState <= Stop5; + end + endrule + + rule transmit_send_stop_bit2(rXmitState == Stop2 && tick); + rXmitDataOut <= 1'b1; + if (rXmitCellCount == 4'hF) begin + rXmitState <= Idle; + pwXmitCellCountReset.send; + end + else begin + rXmitState <= Stop2; + end + endrule + + //////////////////////////////////////////////////////////////////////////////// + /// Interface Connections / Methods + //////////////////////////////////////////////////////////////////////////////// + interface RS232 rs232; + method sout = rXmitDataOut; + method sin = rRecvData._write; + endinterface + + interface Get tx; + method ActionValue#(Bit#(8)) get; + let data = pack(fifoRecv.first); + fifoRecv.deq; + return data; + endmethod + endinterface + + interface Put rx; + method Action put(x); + fifoXmit.enq(x); + endmethod + endinterface + + method Bool transmission_done; + if(!fifoXmit.notEmpty && rXmitState==Idle) + return True; + else + return False; + endmethod + + method Bool receiver_not_empty; + return fifoRecv.notEmpty(); + endmethod + + method Bool receiver_not_full; + return fifoRecv.notFull(); + endmethod + + method Bool transmittor_not_empty; + return fifoXmit.notEmpty(); + endmethod +endmodule + + +endpackage + diff --git a/src/peripherals/uart/Uart16550.bsv b/src/peripherals/uart/Uart16550.bsv new file mode 100644 index 0000000..ae97d23 --- /dev/null +++ b/src/peripherals/uart/Uart16550.bsv @@ -0,0 +1,1164 @@ +/* +Copyright (c) 2013-2017, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +/*- + * Copyright (c) 2013 Simon W. Moore + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * @BERI_LICENSE_HEADER_START@ + * + * Licensed to BERI Open Systems C.I.C. (BERI) under one or more contributor + * license agreements. See the NOTICE file distributed with this work for + * additional information regarding copyright ownership. BERI licenses this + * file to you under the BERI Hardware-Software License, Version 1.0 (the + * "License"); you may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * + * http://www.beri-open-systems.org/legal/license-1-0.txt + * + * Unless required by applicable law or agreed to in writing, Work distributed + * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + * CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * @BERI_LICENSE_HEADER_END@ + * + ****************************************************************************** + * UART16550 + * ========= + * Simon Moore, July 2013 + * + * This Bluespec module implements a 16650 style UART for RS232 serial + * communication. + * + * The following registers exist at 32-bit boundaries accessible in little + * endian byte order: + * + * Offset Name Read/Write Description + * 0 UART_DATA RW write to transmit, read to receive + * 1 UART_INT_ENABLE RW interrupt enable + * 2 UART_INT_ID R interrupt identification + * 2 UART_FIFO_CTRL W FIFO control + * 3 UART_LINE_CTRL RW line control + * 4 UART_MODEM_CTRL W modem control + * 5 UART_LINE_STATUS R line status + * 6 UART_MODEM_STATUS R modem status + * 7 UART_SCRATCH RW scratch register + ******************************************************************************/ + +//Modifications - The Avalon bus has been removed and AXI-4 Lite Bus support has been added + + +package Uart16550; + +import FIFO::*; +import FIFOF::*; +import FIFOLevel::*; +import Clocks::*; +import ClientServer::*; +import defined_types::*; +import AXI4_Lite_Types::*; +import AXI4_Lite_Fabric::*; +import Semi_FIFOF::*; +`include "instance_defines.bsv" + +// depth of transmit and receive FIFOs +typedef 16 Tx_FIFO_depth; +typedef 16 Rx_FIFO_depth; + +// enumerate addresses corresponding to device registers +typedef enum { + UART_ADDR_DATA=0, + UART_ADDR_INT_ENABLE=1, + UART_ADDR_INT_ID_FIFO_CTRL=2, // read=INT_ID, write=FIFO_CTRL + UART_ADDR_LINE_CTRL=3, + UART_ADDR_MODEM_CTRL=4, + UART_ADDR_LINE_STATUS=5, + UART_ADDR_MODEM_STATUS=6, + UART_ADDR_SCRATCH=7 + } UART_ADDR_T deriving (Bits, Eq, FShow); + +// interrupt enable register bits +typedef struct { + Bool uart_IE_MS; // Modem status interrupt + Bool uart_IE_RLS; // Receiver line status interrupt + Bool uart_IE_THRE; // Transmitter holding register empty interrupt + Bool uart_IE_RDA; // Recived data available interrupt + } UART_IE_T deriving (Bits, Eq, FShow); + +// interrupt identification values +typedef enum { + UART_II_MS = 4'b0000, // modem status + UART_II_NO_INT = 4'b0001, // no interrupt pending + UART_II_THRE = 4'b0010, // transmitter holding register empty + UART_II_RDA = 4'b0100, // receiver data available + UART_II_RLS = 4'b0110, // receiver line status + UART_II_TI = 4'b1100 // timeout indication + } UART_II_T deriving (Bits, Eq, FShow); + +// line control register bits +typedef struct { + Bit#(1) uart_LC_DL; // divisor latch access bit + Bit#(1) uart_LC_BC; // break control + Bit#(1) uart_LC_SP; // stick parity + Bit#(1) uart_LC_EP; // even parity + Bit#(1) uart_LC_PE; // parity enables + Bit#(1) uart_LC_SB; // stop bits + Bit#(2) uart_LC_BITS; // bits in character + } UART_LC_T deriving (Bits, Eq, FShow); + +// modem control register bits +typedef struct { + bit uart_MC_LOOPBACK; + bit uart_MC_OUT2; + bit uart_MC_OUT1; + bit uart_MC_RTS; + bit uart_MC_DTR; + } UART_MC_T deriving (Bits, Eq, FShow); + +// line status register bits +typedef struct { + Bool uart_LS_EI; // error indicator + Bool uart_LS_TW; // transmitter empty indicator + Bool uart_LS_TFE; // transmitter FIFO is empty + Bool uart_LS_BI; // break interrupt + Bool uart_LS_FE; // framing error + Bool uart_LS_PE; // parity error + Bool uart_LS_OE; // overrun error + Bool uart_LS_DR; // data ready + } UART_LS_T deriving (Bits, Eq, FShow); + +// modem status register bits +typedef struct { + bit uart_MS_CDCD; // complement signals + bit uart_MS_CRI; + bit uart_MS_CDSR; + bit uart_MS_CCTS; + bit uart_MS_DDCD; // delta signals + bit uart_MS_TERI; + bit uart_MS_DDSR; + bit uart_MS_DCTS; + } UART_MS_T deriving (Bits, Eq, FShow); + +// data from receiver +typedef struct { + Bit#(8) data; + Bool break_error; + Bool parity_error; + Bool framing_error; + } RX_DATA_T deriving (Bits, Eq); + +// transmitter states +typedef enum { + STX_idle, STX_pop_byte, STX_send_start, STX_send_byte, STX_send_parity, STX_send_stop + } TX_state_T deriving (Bits, Eq, FShow); + +// receiver states +typedef enum { + SRX_idle, SRX_rec_start, SRX_rec_bit, SRX_rec_parity, SRX_rec_stop, + SRX_check_parity, SRX_rec_prepare, SRX_end_bit, SRX_wait1, + SRX_ca_lc_parity, SRX_push } RX_state_T deriving (Bits, Eq, FShow); + + +(* always_ready, always_enabled *) +interface RS232_PHY_Ifc; + (* always_ready, always_enabled *) method Action modem_input(bit srx, bit cts, bit dsr, bit ri, bit dcd); + method bit modem_output_stx; + method bit modem_output_rts; + method bit modem_output_dtr; +endinterface + + +interface Uart16550_AXI4_Lite_Ifc; + interface RS232_PHY_Ifc coe_rs232; + interface AXI4_Lite_Slave_IFC#(`PADDR,`Reg_width,`USERSPACE) slave_axi_uart; + (* always_ready, always_enabled *) method bit irq; +endinterface + + +(* synthesize, + reset_prefix = "csi_clockreset_reset_n", + clock_prefix = "csi_clockreset_clk" *) +module mkUart16550#(Clock core_clock, Reset core_reset)(Uart16550_AXI4_Lite_Ifc); + AXI4_Lite_Slave_Xactor_IFC #(`PADDR,`Reg_width,`USERSPACE) s_xactor <- mkAXI4_Lite_Slave_Xactor(clocked_by core_clock, reset_by core_reset); + UART_transmitter_ifc uart_tx <- mkUART_transmitter; + UART_receiver_ifc uart_rx <- mkUART_receiver; + + // TODO: FIXME: use Tx_FIFO_depth and Rx_FIFO_depth rather than 16? + // TX should only have a 1 element FIFO + // FIFOCountIfc#(Bit#(8), 16) tx_fifo <- mkGFIFOCount(True, False, True); + FIFOF#(Bit#(8)) tx_fifo <- mkGFIFOF1(True, False); + FIFOCountIfc#(RX_DATA_T, 16) rx_fifo <- mkGFIFOCount(True, True, True); + PulseWire tx_fifo_clear_pw <- mkPulseWire; + PulseWire rx_fifo_clear_pw <- mkPulseWire; + // add some bypass wires to hack around scheduling loop + Wire#(Bool) rx_fifo_full <- mkBypassWire; + Wire#(Bool) rx_fifo_empty <- mkBypassWire; + Wire#(Bool) tx_fifo_empty <- mkBypassWire; + // provide first item of rx_fifo if there is one, otherwise a default + Wire#(RX_DATA_T) rx_fifo_first <- mkBypassWire; + + PulseWire count_error_up <- mkPulseWire; + PulseWire count_error_down <- mkPulseWire; + PulseWire count_error_clear <- mkPulseWire; + Reg#(UInt#(TAdd#(Rx_FIFO_depth,1))) + count_error <- mkReg(0); + + Reg#(Bit#(2)) fcr <- mkReg(2'b11); // upper 2 bits of FIFO control register (rest not stored) + Reg#(UART_IE_T) ier <- mkReg(unpack(0)); // interrupt enable register bits (disable after reset) + Reg#(UART_LC_T) lcr <- mkReg(unpack('b00000011)); // line control register (default 8n1 format) + Reg#(UART_MC_T) mcr <- mkReg(unpack(0)); // modem control register + Wire#(UART_MC_T) mc_bypass <- mkBypassWire; + Reg#(UART_LS_T) lsr <- mkReg(unpack(0)); // line status register + Reg#(UART_MS_T) msr <- mkReg(unpack(0)); // modem status register + Reg#(Bit#(8)) scratch <- mkReg(unpack(0)); // scratch register + + Wire#(Bool) loopback <- mkBypassWire; // loopback mode (msr[4]) + + Reg#(Bit#(8)) dl1r <- mkReg(0); // divisor 1 register + Reg#(Bit#(8)) dl2r <- mkReg(0); // divisor 2 register + Reg#(Bit#(16)) dlc <- mkReg(0); // divisor counter + Reg#(Bit#(16)) dl <- mkReg(0); // divisor counter bound + Reg#(Bool) enable <- mkReg(False); + Wire#(Maybe#(Bit#(16))) + dl_update <- mkDWire(tagged Invalid); + + PulseWire interrupt_pw <- mkPulseWireOR; + RS_ifc rls_int <- mkRS; + RS_ifc rda_int <- mkRS; + RS_ifc thre_int <- mkRS; + RS_ifc ms_int <- mkRS; + RS_ifc ti_int <- mkRS; + + // synchroniser registers for input pins + Reg#(bit) pin_srx_sync <- mkReg(0); + Reg#(bit) pin_cts_sync <- mkReg(0); + Reg#(bit) pin_dsr_sync <- mkReg(0); + Reg#(bit) pin_ri_sync <- mkReg(0); + Reg#(bit) pin_dcd_sync <- mkReg(0); + + // registers for stable input pin values pre loopback check + Reg#(bit) pin_srx_c <- mkReg(0); + Reg#(bit) pin_cts_c <- mkReg(0); + Reg#(bit) pin_dsr_c <- mkReg(0); + Reg#(bit) pin_ri_c <- mkReg(0); + Reg#(bit) pin_dcd_c <- mkReg(0); + + // registers for stable input pin values + Reg#(bit) pin_srx <- mkReg(0); + Reg#(bit) pin_cts <- mkReg(0); + Reg#(bit) pin_dsr <- mkReg(0); + Reg#(bit) pin_ri <- mkReg(0); + Reg#(bit) pin_dcd <- mkReg(0); + + // previous pin values last read via MSR (modem status register) + Reg#(bit) prev_cts <- mkReg(0); + Reg#(bit) prev_dsr <- mkReg(0); + Reg#(bit) prev_ri <- mkReg(0); + Reg#(bit) prev_dcd <- mkReg(0); + PulseWire msr_save_pin_state <- mkPulseWire; // trigger condition to save pin state + + // registered outputs + Reg#(bit) pin_stx <- mkReg(0); + Reg#(bit) pin_rts <- mkReg(0); + Reg#(bit) pin_dtr <- mkReg(0); + + SyncFIFOIfc#(AXI4_Lite_Rd_Addr #(`PADDR,`USERSPACE)) ff_rd_addr <- mkSyncFIFOToCC(1,core_clock,core_reset); + SyncFIFOIfc#(AXI4_Lite_Wr_Addr #(`PADDR, `USERSPACE)) ff_wr_addr <- mkSyncFIFOToCC(1,core_clock,core_reset); + SyncFIFOIfc#(AXI4_Lite_Wr_Data #(`Reg_width)) ff_wr_data <- mkSyncFIFOToCC(1,core_clock,core_reset); + + SyncFIFOIfc#(AXI4_Lite_Rd_Data #(`Reg_width,`USERSPACE)) ff_rd_resp <- mkSyncFIFOFromCC(1,core_clock); + SyncFIFOIfc#(AXI4_Lite_Wr_Resp #(`USERSPACE)) ff_wr_resp <- mkSyncFIFOFromCC(1,core_clock); + + + (* no_implicit_conditions *) + rule synchronise_input_pins; // N.B. there must be no logic between these registers + pin_srx_c <= pin_srx_sync; + pin_cts_c <= pin_cts_sync; + pin_dsr_c <= pin_dsr_sync; + pin_ri_c <= pin_ri_sync; + pin_dcd_c <= pin_dcd_sync; + endrule + + rule bypass_mrc_to_avoid_scheduling_loop; + mc_bypass <= mcr; + endrule + + (* no_implicit_conditions *) + rule handle_loopback_mode; + if(loopback) + begin + pin_srx <= pin_stx; + pin_cts <= mc_bypass.uart_MC_RTS; + pin_dsr <= mc_bypass.uart_MC_DTR; + pin_ri <= mc_bypass.uart_MC_OUT1; + pin_dcd <= mc_bypass.uart_MC_OUT2; + end + else + begin + pin_srx <= pin_srx_c; + pin_cts <= pin_cts_c; + pin_dsr <= pin_dsr_c; + pin_ri <= pin_ri_c; + pin_dcd <= pin_dcd_c; + end + + msr <= UART_MS_T{ + // first changes in the pins + uart_MS_DCTS: pin_cts ^ prev_cts, + uart_MS_DDSR: pin_dsr ^ prev_dsr, + uart_MS_TERI: pin_ri ^ prev_ri, + uart_MS_DDCD: pin_dcd ^ prev_dcd, + // then the actual signals + uart_MS_CCTS: pin_cts, // TODO: allow this to be from loopback + uart_MS_CDSR: pin_dsr, + uart_MS_CRI: pin_ri, + uart_MS_CDCD: pin_dcd}; + + if(msr_save_pin_state) + begin + prev_dcd <= pin_dcd; + prev_ri <= pin_ri; + prev_dsr <= pin_dsr; + prev_cts <= pin_cts; + end + endrule + + (* no_implicit_conditions *) + rule output_rts_dtr; + pin_rts <= mcr.uart_MC_RTS; + pin_dtr <= mcr.uart_MC_DTR; + endrule + + (* no_implicit_conditions *) + rule loopback_mode_select; + loopback <= mcr.uart_MC_LOOPBACK==1; + endrule + + (* no_implicit_conditions *) + rule connect_pins_rx; + uart_rx.input_srx(pin_srx); + endrule + (* no_implicit_conditions *) + rule connect_pins_tx; + pin_stx <= uart_tx.output_stx; + endrule + (* no_implicit_conditions *) + rule rx_first_item_if_any; + rx_fifo_first <= rx_fifo.notEmpty ? rx_fifo.first + : RX_DATA_T{ + data:0, + break_error: False, + parity_error: False, + framing_error: False}; + endrule + + (* no_implicit_conditions *) + rule interrupt_sources; + if(rda_int.state || rls_int.state || thre_int.state || ms_int.state || ti_int.state) + interrupt_pw.send; + + // receiver line status interrupt + // - note: also reset on read of line status + if(!ier.uart_IE_RLS) + rls_int.reset; + else if(rx_fifo_full + || rx_fifo_first.parity_error + || rx_fifo_first.framing_error + || rx_fifo_first.break_error) + rls_int.posedge_set; + + // received data available interrupt + UInt#(5) trigger_level; + case(fcr) + // 2'b00 handled by default case + 2'b01 : trigger_level = 4; + 2'b10 : trigger_level = 8; + 2'b11 : trigger_level = 14; + default : trigger_level = 1; + endcase + // TODO: should this in fact be edge triggered on the trigger level being reached or passed? + if(ier.uart_IE_RDA && !rx_fifo_empty && (rx_fifo.count >= trigger_level)) + rda_int.set; + else + rda_int.reset; + + // transmitter holding register empty interrupt +// if(!ier.uart_IE_THRE) + if(!ier.uart_IE_THRE || !tx_fifo_empty) + thre_int.reset; + else if(tx_fifo_empty) + thre_int.posedge_set; + + // timer interrupt + if(!ier.uart_IE_RDA) + ti_int.reset; + else if(uart_rx.timeout) + ti_int.posedge_set; + + // modem status interrupt + // - note: also reset by reading modem status + if(!ier.uart_IE_MS) + ms_int.reset; + else if({msr.uart_MS_DCTS, msr.uart_MS_DDSR, msr.uart_MS_TERI, msr.uart_MS_DDCD} != 0) + ms_int.posedge_set; + endrule + + (* no_implicit_conditions *) + rule foward_lc_enable; + uart_tx.control(lcr, enable); + uart_rx.control(lcr, enable); + endrule + + (* no_implicit_conditions *) + rule divisor_counter; + enable <= (dlc==0) && (dl>0); + if(isValid(dl_update)) + begin + let newdl = fromMaybe(?, dl_update); + dl <= newdl; + dlc <= newdl-1; + `ifdef verbose $display("%05t: dl set to %1d", $time, newdl); `endif + end + else + dlc <= (dlc==0 ? dl : dlc) - 1; + endrule + + (* no_implicit_conditions *) + rule forward_tx_clear(tx_fifo_clear_pw); + tx_fifo.clear; + endrule + rule forward_tx(!tx_fifo_clear_pw && tx_fifo.notEmpty); + uart_tx.tx_char(tx_fifo.first); + tx_fifo.deq; + endrule + + rule forward_rx; + if(rx_fifo_clear_pw) + rx_fifo.clear; + else if(rx_fifo.notFull) + begin + RX_DATA_T rx <- uart_rx.rx_char; + rx_fifo.enq(rx); + if(rx.break_error || rx.parity_error || rx.framing_error) + count_error_up.send(); + end + endrule + + (* no_implicit_conditions *) + rule count_rx_errors; + if(count_error_clear) + count_error <= 0; + else + begin + if(count_error_up && !count_error_down && (count_error0)) + count_error <= count_error-1; + end + endrule + + (* no_implicit_conditions *) + rule fifo_status_bypass_to_avoid_scheduling_loop; + rx_fifo_full <= !rx_fifo.notFull; + rx_fifo_empty <= !rx_fifo.notEmpty; + tx_fifo_empty <= !tx_fifo.notEmpty; + endrule + + rule capture_read_request; + let req <- pop_o (s_xactor.o_rd_addr); + ff_rd_addr.enq(req); + endrule + rule send_read_respone_to_bus; + s_xactor.i_rd_data.enq(ff_rd_resp.first); + ff_rd_resp.deq; + endrule + rule capture_write_request; + let req <- pop_o (s_xactor.o_wr_addr); + let wr_data <- pop_o(s_xactor.o_wr_data); + ff_wr_addr.enq(req); + ff_wr_data.enq(wr_data); + endrule + rule send_write_response; + s_xactor.i_wr_resp.enq(ff_wr_resp.first); + ff_wr_resp.deq; + endrule + + + rule handle_axi4_read(ff_rd_addr.notEmpty); + + Bool dlab = lcr.uart_LC_DL == 1'b1; // divisor latch enable + let req = ff_rd_addr.first; + ff_rd_addr.deq; + `ifdef verbose $display("RD_ADDR %h", req.araddr); `endif + UART_ADDR_T addr = unpack(req.araddr[5:3]); + Bool rtn_valid=True; + Bit#(8) rtn = 0; + + let ls = UART_LS_T{ + uart_LS_EI: rx_fifo_full || (count_error!=0), // error indicator + uart_LS_TW: tx_fifo_empty && uart_tx.tx_buf_empty, // transmitter empty + uart_LS_TFE: tx_fifo_empty, // transmitter FIFO empty + uart_LS_BI: rx_fifo_first.break_error, // break error + uart_LS_FE: rx_fifo_first.framing_error, // framing error + uart_LS_PE: rx_fifo_first.parity_error, // parity error + uart_LS_OE: rx_fifo_full, // overflow + uart_LS_DR: !rx_fifo_empty}; // data ready + + lsr <= ls; + + UART_II_T ii; + if(rls_int.state) // highest priority interrupt - receiver line status + ii = UART_II_RLS; + else if(rda_int.state) // second priority interrupt - received data available + ii = UART_II_RDA; + else if(ti_int.state) // also second priority - timeout + ii = UART_II_TI; + else if(thre_int.state) // third priority - transmitter holding register empty + ii = UART_II_THRE; + else if(ms_int.state) // fourth - modem status change interrupt + ii = UART_II_MS; + else + ii = UART_II_NO_INT; + `ifdef verbose $display("addr_READ: %d",addr) ; `endif + case(addr) + UART_ADDR_DATA : if(dlab) // divisor latch enabled + rtn = dl1r; + else if(!rx_fifo_empty) + begin + RX_DATA_T rx = rx_fifo.first; + rtn = rx.data; + if(rx.break_error || rx.parity_error || rx.framing_error) + count_error_down.send; + rx_fifo.deq; + ti_int.reset; + rda_int.reset; + end + else + rtn_valid = False; // TODO: should this be the old value? + UART_ADDR_INT_ENABLE : rtn = dlab ? dl2r : zeroExtend(pack(ier)); + UART_ADDR_INT_ID_FIFO_CTRL : rtn = {4'b1100, pack(ii)}; + UART_ADDR_LINE_CTRL : rtn = pack(lcr); + UART_ADDR_MODEM_CTRL : rtn = zeroExtend(pack(mcr)); + UART_ADDR_LINE_STATUS : begin + rls_int.reset; + rtn = pack(ls); + end + UART_ADDR_MODEM_STATUS : begin + ms_int.reset; + rtn = pack(msr); + msr_save_pin_state.send(); + end + UART_ADDR_SCRATCH : rtn = scratch; + + endcase + let resp = AXI4_Lite_Rd_Data {rresp : AXI4_LITE_OKAY, rdata : rtn_valid? zeroExtend(rtn) : '1, ruser: 0}; + ff_rd_resp.enq(resp); + // $display ("DATA----------- %b", rtn); + `ifdef verbose $display("%05t: --------------------------READ--------------------------------------------",$time); `endif + endrule + + rule handle_axi4_write(ff_wr_addr.notEmpty && ff_wr_data.notEmpty); + Bool dlab = lcr.uart_LC_DL == 1'b1; // divisor latch enable + let ls = UART_LS_T{ + uart_LS_EI: rx_fifo_full || (count_error!=0), // error indicator + uart_LS_TW: tx_fifo_empty && uart_tx.tx_buf_empty, // transmitter empty + uart_LS_TFE: tx_fifo_empty, // transmitter FIFO empty + uart_LS_BI: rx_fifo_first.break_error, // break error + uart_LS_FE: rx_fifo_first.framing_error, // framing error + uart_LS_PE: rx_fifo_first.parity_error, // parity error + uart_LS_OE: rx_fifo_full, // overflow + uart_LS_DR: !rx_fifo_empty}; // data ready + + lsr <= ls; + + UART_II_T ii; + if(rls_int.state) // highest priority interrupt - receiver line status + ii = UART_II_RLS; + else if(rda_int.state) // second priority interrupt - received data available + ii = UART_II_RDA; + else if(ti_int.state) // also second priority - timeout + ii = UART_II_TI; + else if(thre_int.state) // third priority - transmitter holding register empty + ii = UART_II_THRE; + else if(ms_int.state) // fourth - modem status change interrupt + ii = UART_II_MS; + else + ii = UART_II_NO_INT; + + let wr_addr = ff_wr_addr.first; + `ifdef verbose $display("WR_ADDR %h", wr_addr.awaddr); `endif + ff_wr_addr.deq; + let wr_data = ff_wr_data.first; + ff_wr_data.deq; + `ifdef verbose $display("WR_DATA %h", wr_data.wdata); `endif + UART_ADDR_T addr = unpack(wr_addr.awaddr[5:3]); + Bit#(8) d = truncate(pack(wr_data.wdata)); + Bit#(8) rtn=0; + Bool rtn_valid=True; + `ifdef verbose $display("addr_WRITE: %d",addr); `endif + case(addr) + UART_ADDR_DATA: if(dlab) // divisor latch enabled + begin + dl1r <= d; + dl_update <= tagged Valid ({dl2r,d}); + end + else if(tx_fifo.notFull) + begin + tx_fifo.enq(unpack(d)); + thre_int.reset; + end + UART_ADDR_INT_ENABLE: if(dlab) + dl2r <= unpack(d); + else + ier <= unpack(truncate(d)); + UART_ADDR_INT_ID_FIFO_CTRL: begin + fcr <= d[7:6]; + if(d[1]==1'b1) + begin + rx_fifo_clear_pw.send; + count_error_clear.send; + end + if(d[2]==1'b1) + tx_fifo_clear_pw.send; + end + UART_ADDR_LINE_CTRL : lcr <= unpack(truncate(pack(wr_data.wdata))); + UART_ADDR_MODEM_CTRL : mcr <= unpack(truncate(pack(wr_data.wdata))); + UART_ADDR_LINE_STATUS : begin /* no write */ end + UART_ADDR_MODEM_STATUS : begin /* no write */ end + UART_ADDR_SCRATCH : begin scratch <= d; `ifdef verbose $display("scratch : %h",d); `endif end + endcase + let resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_OKAY, buser: 0}; + ff_wr_resp.enq(resp); + // $display ("DATA WRITE----------- %b", wr_data.wdata); + // $display ("DATA Adress----------- %d", addr); + `ifdef verbose $display("%05t: ----------------------------WRITE------------------------------------------",$time); `endif + endrule + /* + rule trans ; + $display("%05t: tx bit = %b", $time, pin_stx); + endrule + */ + interface RS232_PHY_Ifc coe_rs232; + method Action modem_input(bit srx, bit cts, bit dsr, bit ri, bit dcd); + pin_srx_sync <= srx; // RX Input + pin_cts_sync <= cts; // CTS Input + pin_dsr_sync <= dsr; // Data Set Ready indicating that MODEM is ready to establish the communication + pin_ri_sync <= ri; // Ring Indicator indicate that a telephone ringing signal has been recieved by the MODEM + pin_dcd_sync <= dcd; // Data carrier detect + endmethod + method bit modem_output_stx = pin_stx; // Tx output + method bit modem_output_rts = pin_rts; // RTS output + method bit modem_output_dtr = pin_dtr; // Data Terminal Ready output + endinterface + + interface slave_axi_uart = s_xactor.axi_side; + + method bit irq; + return interrupt_pw ? 1'b1 : 1'b0; + endmethod + +endmodule + + + + +////////////////////////////////////////////////////////////////////////////// +// transmitter + +interface UART_transmitter_ifc; + method Action tx_char(Bit#(8) c); + (* always_ready, always_enabled *) + method Bool tx_buf_empty; + (* always_ready, always_enabled *) + method Action control(UART_LC_T lc_in, Bool enable_in); + (* always_ready, always_enabled *) + method bit output_stx; +endinterface + + +module mkUART_transmitter(UART_transmitter_ifc); + + FIFOF#(Bit#(8)) tx_fifo <- mkLFIFOF; + Wire#(Bool) tx_fifo_empty <- mkBypassWire; + Reg#(bit) bit_out <- mkReg(0); + Reg#(bit) parity_xor <- mkReg(0); + Reg#(bit) stx_o_tmp <- mkReg(1); // rename output bit? our use bit_out directly? + Reg#(TX_state_T) tstate <- mkReg(STX_idle); + Reg#(TX_state_T) last_tstate <- mkReg(STX_idle); + Reg#(UInt#(5)) counter <- mkReg(0); + Reg#(UInt#(3)) bit_counter <- mkReg(0); + Reg#(Bit#(7)) shift_out <- mkReg(0); + Wire#(UART_LC_T) lc <- mkBypassWire; + Wire#(Bool) enable <- mkBypassWire; + + rule monitor_state_for_debug(last_tstate != tstate); + + `ifdef verbose $write("%05t: UART TX state change ", $time); `endif + `ifdef verbose $write(fshow(last_tstate)); `endif + `ifdef verbose $write(" -> "); `endif + `ifdef verbose $display(fshow(tstate)); `endif + + last_tstate <= tstate; + endrule + + // rule to decouple rule dependency on tx_fifo.notEmpty + (* no_implicit_conditions *) + rule forward_tx_fifo_empty; + tx_fifo_empty <= !tx_fifo.notEmpty; + endrule + + rule idle(enable && (tstate==STX_idle)); + tstate <= STX_pop_byte; // move directly to pop_byte since it will block if the tx_fifo is empty + stx_o_tmp <= 1; + endrule + + rule pop_byte(enable && (tstate==STX_pop_byte)); + case(lc.uart_LC_BITS) // number of bits in a word + 0: begin + bit_counter <= 4; + parity_xor <= ^tx_fifo.first[4:0]; + end + 1: begin + bit_counter <= 5; + parity_xor <= ^tx_fifo.first[5:0]; + end + 2: begin + bit_counter <= 6; + parity_xor <= ^tx_fifo.first[6:0]; + end + 3: begin + bit_counter <= 7; + parity_xor <= ^tx_fifo.first[7:0]; + end + endcase + shift_out[6:0] <= tx_fifo.first[7:1]; + bit_out <= tx_fifo.first[0]; + tstate <= STX_send_start; + endrule + + rule send_start(enable && (tstate==STX_send_start)); + + if(counter==0) + counter <= 5'b01111; + else if(counter==1) + begin + counter <= 0; + tstate <= STX_send_byte; + end + else + counter <= counter-1; + stx_o_tmp <= 0; + endrule + + rule send_byte(enable && (tstate==STX_send_byte)); + if(counter==0) + counter <= 5'b01111; + else if(counter==1) + begin + if(bit_counter > 0) + begin + bit_counter <= bit_counter-1; + shift_out <= {1'b0,shift_out[6:1]}; + bit_out <= shift_out[0]; + end + else // end of byte + if(lc.uart_LC_PE == 0) // no partity bit + tstate <= STX_send_stop; + else + begin + case({lc.uart_LC_EP, lc.uart_LC_SP}) + 2'b00: bit_out <= ~parity_xor; + 2'b01: bit_out <= 1; + 2'b10: bit_out <= parity_xor; + 2'b11: bit_out <= 0; + endcase + tstate <= STX_send_parity; + end + counter <= 0; + end + else + counter <= counter-1; + stx_o_tmp <= bit_out; + endrule + + rule send_parity(enable && (tstate==STX_send_parity)); + if(counter==0) + counter <= 5'b01111; + else if(counter==1) + begin + counter <= 0; + tstate <= STX_send_stop; + end + else + counter <= counter-1; + stx_o_tmp <= bit_out; + + endrule + + rule send_stop(enable && (tstate==STX_send_stop)); + if(counter==0) + counter <= lc.uart_LC_SB==0 ? 5'b01101 : // 1 stop bit + lc.uart_LC_BITS==0 ? 5'b10101 : // 1.5 stop bits + 5'b11101; // 2 stop bits + else if(counter==1) + begin + counter <= 0; + tstate <= STX_idle; + tx_fifo.deq; + end + else + counter <= counter-1; + stx_o_tmp <= 1; + endrule + + + method Action tx_char(Bit#(8) c); + tx_fifo.enq(c); + endmethod + + method Bool tx_buf_empty = tx_fifo_empty; + + method Action control(UART_LC_T lc_in, Bool enable_in); + lc <= lc_in; + enable <= enable_in; + endmethod + + method bit output_stx = lc.uart_LC_BC==1 ? 0 : stx_o_tmp; // handle break condition + +endmodule + + +////////////////////////////////////////////////////////////////////////////// +// receiver + +interface UART_receiver_ifc; + method ActionValue#(RX_DATA_T) rx_char(); + (* always_ready, always_enabled *) + method Bool timeout(); + (* always_ready, always_enabled *) + method Action control(UART_LC_T lc_in, Bool enable_in); + (* always_ready, always_enabled *) + method Action input_srx(bit rx); +endinterface + + +module mkUART_receiver(UART_receiver_ifc); + + FIFOF#(RX_DATA_T) rx_fifo <- mkLFIFOF; + Reg#(bit) rx_stable <- mkReg(1); + Wire#(UART_LC_T) lc <- mkBypassWire; + Wire#(Bool) enable <- mkBypassWire; + Reg#(RX_state_T) rstate <- mkReg(SRX_idle); + Reg#(RX_state_T) last_rstate <- mkReg(SRX_idle); + Reg#(UInt#(4)) rcounter <- mkReg(0); + Reg#(UInt#(3)) rbit_counter <- mkReg(0); + Reg#(Bit#(8)) rshift <- mkReg(0); + Reg#(bit) rparity <- mkReg(0); + Reg#(bit) rparity_error <- mkReg(0); + Reg#(bit) rframing_error <- mkReg(0); + Reg#(bit) rparity_xor <- mkReg(0); + Reg#(UInt#(8)) counter_b <- mkReg(159); + Reg#(UInt#(10)) counter_t <- mkReg(511); + PulseWire counter_t_preset <- mkPulseWireOR; + + Bool break_error = counter_b==0; + + rule monitor_state_for_debug(last_rstate != rstate); + + `ifdef verbose $write("%05t: UART RX state change ", $time); `endif + `ifdef verbose $write(fshow(last_rstate)); `endif + `ifdef verbose $write(" -> "); `endif + `ifdef verbose $display(fshow(rstate)); `endif + + last_rstate <= rstate; + endrule + + (* no_implicit_conditions *) + rule receive_status_counters; + UInt#(10) toc_value; + case ({lc.uart_LC_PE, lc.uart_LC_SB, lc.uart_LC_BITS}) + 4'b0000: toc_value = 447; // 7 bits + 4'b0100: toc_value = 479; // 7.5 bits + 4'b0001, + 4'b1000: toc_value = 511; // 8 bits + 4'b1100: toc_value = 543; // 8.5 bits + 4'b0010, + 4'b0101, + 4'b1001: toc_value = 575; // 9 bits + 4'b0011, + 4'b0110, + 4'b1010, + 4'b1101: toc_value = 639; // 10 bits + 4'b0111, + 4'b1011, + 4'b1110: toc_value = 703; // 11 bits + 4'b1111: toc_value = 767; // 12 bits + default: toc_value = 511; // 8 bits + endcase + + UInt#(8) brc_value = truncate(toc_value>>2); // break counter value + + if(rx_stable==1) + counter_b <= brc_value; + else if((counter_b!=0) && enable) + counter_b <= counter_b-1; + + if(counter_t_preset) + counter_t <= toc_value; + else if(enable && (counter_t!=0)) + counter_t <= counter_t - 1; + endrule + + // helper rule to decouple firing dependancies + rule couter_t_preset_on_fifo_empty(!rx_fifo.notEmpty); + counter_t_preset.send(); + endrule + + (* no_implicit_conditions *) + rule idle(enable && (rstate==SRX_idle)); + rcounter <= 4'b1110; + if((rx_stable==0) && !break_error) + rstate <= SRX_rec_start; + endrule + + rule rec_start(enable && (rstate==SRX_rec_start)); + if(rcounter==7) + if(rx_stable==1) // no start bit + rstate <= SRX_idle; + else + rstate <= SRX_rec_prepare; + rcounter <= rcounter-1; + endrule + + rule rec_prepare(enable && (rstate==SRX_rec_prepare)); + rbit_counter <= unpack(zeroExtend(lc.uart_LC_BITS) + 4); + if(rcounter==0) + begin + rstate <= SRX_rec_bit; + rcounter <= 4'b1110; + rshift <= 0; + end + else + rcounter <= rcounter-1; + endrule + + rule rec_bit(enable && (rstate==SRX_rec_bit)); + if(rcounter==0) + rstate <= SRX_end_bit; + if(rcounter==7) // read the bit + case(lc.uart_LC_BITS) // number of bits in a word + 0: rshift[4:0] <= {rx_stable, rshift[4:1]}; + 1: rshift[5:0] <= {rx_stable, rshift[5:1]}; + 2: rshift[6:0] <= {rx_stable, rshift[6:1]}; + 3: rshift[7:0] <= {rx_stable, rshift[7:1]}; + endcase + rcounter <= rcounter-1; + endrule + + rule end_bit(enable && (rstate==SRX_end_bit)); + if(rbit_counter==0) // no more bits in the word + begin + rstate <= (lc.uart_LC_PE==1) ? SRX_rec_parity : SRX_rec_stop; + rparity_error <= 0; + end + else + rstate <= SRX_rec_bit; + rbit_counter <= rbit_counter-1; + rcounter <= rcounter-1; + endrule + + rule rec_parity(enable && (rstate==SRX_rec_parity)); + if(rcounter == 7) // read parity + begin + rparity <= rx_stable; + rstate <= SRX_ca_lc_parity; + end + rcounter <= rcounter-1; + //$display("%05t rx bit = %d", $time, rx_stable); + endrule + /* + rule recie ; + $display("%05t: rx bit = %d", $time, rx_stable); + endrule + */ + rule calc_parity(enable && (rstate==SRX_ca_lc_parity)); + rparity_xor <= ^{rshift, rparity}; + rstate <= SRX_check_parity; + rcounter <= rcounter-1; + endrule + + rule check_parity(enable && (rstate==SRX_check_parity)); + case({lc.uart_LC_EP, lc.uart_LC_SP}) + 2'b00: rparity_error <= ~rparity_xor; + 2'b01: rparity_error <= ~rparity; + 2'b10: rparity_error <= rparity_xor; + 2'b11: rparity_error <= rparity; + endcase + rcounter <= rcounter-1; + rstate <= SRX_wait1; + endrule + + rule wait1(enable && (rstate==SRX_wait1)); + if(rcounter==0) + begin + rcounter <= 4'b1110; + rstate <= SRX_rec_stop; + end + else + rcounter <= rcounter-1; + endrule + + rule rec_stop(enable && (rstate==SRX_rec_stop)); + if(rcounter==7) // read the stop bit + begin + rframing_error <= ~rx_stable; // no framing error if stop bit = 1 + rstate <= SRX_push; + end + rcounter <= rcounter-1; + `ifdef verbose $display("%05t: rx bit = %d", $time, rx_stable); `endif + endrule + + rule push(enable && (rstate==SRX_push)); + if((rx_stable==1) || break_error) + begin + rstate <= SRX_idle; + if(break_error) + rx_fifo.enq( + RX_DATA_T{ + data: 8'b0, + break_error: True, + parity_error: True, + framing_error: False + } + ); + else + rx_fifo.enq( + RX_DATA_T{ + data: rshift, + break_error: False, + parity_error: rparity_error==1, + framing_error: rframing_error==1 + } + ); + counter_t_preset.send; // preset counter_t on an enq + //$display("%05t: rx bit = %d", $time, rx_stable); + end + endrule + + method ActionValue#(RX_DATA_T) rx_char(); + counter_t_preset.send; // preset counter_t on a deq + rx_fifo.deq; + return rx_fifo.first; + endmethod + method Bool timeout() = counter_t==0; + method Action control(UART_LC_T lc_in, Bool enable_in); + lc <= lc_in; + enable <= enable_in; + endmethod + method Action input_srx(bit rx); + rx_stable <= rx; + endmethod +endmodule + + +////////////////////////////////////////////////////////////////////////////// +// clocked RS (reset/set) flip-flow with reset dominating and edge triggering set + +/* +(* always_ready, always_enabled *) +interface RS_ifc; + method Action set; + method Action reset; + method Action enable(Bool en); + method Bool state; +endinterface + + +module mkRS(RS_ifc); + PulseWire s <- mkPulseWire; + PulseWire r <- mkPulseWireOR; + Wire#(Bool) e <- mkBypassWire; + Wire#(Bool) q_next <- mkBypassWire; + Reg#(Bool) q <- mkReg(False); + Reg#(Bool) s_prev <- mkReg(False); + + (* no_implicit_conditions *) + rule handle_state_update; + Bool s_rise = s && !s_prev; + q_next <= e && !r && (s_rise || q); + q <= q_next; + s_prev <= s; + endrule + + method Action set; s.send(); endmethod + method Action reset; r.send(); endmethod + method Bool state = q_next; + method Action enable(Bool en); + e <= en; + endmethod + +endmodule +*/ + + +(* always_ready, always_enabled *) +interface RS_ifc; + method Action set; + method Action reset; + method Action posedge_set; + method Action posedge_reset; + method Bool state; +endinterface + +module mkRS(RS_ifc); + PulseWire s <- mkPulseWireOR; + PulseWire r <- mkPulseWireOR; + PulseWire edge_s <- mkPulseWireOR; + PulseWire edge_r <- mkPulseWireOR; + + Reg#(Bool) q <- mkReg(False); + Reg#(Bool) s_prev <- mkReg(False); + Reg#(Bool) r_prev <- mkReg(False); + + + (* no_implicit_conditions *) + rule handle_edges_history; + s_prev <= s; + r_prev <= r; + endrule + + (* no_implicit_conditions *) + rule handle_edges_set; + if(edge_s && !s_prev) s.send; + if(edge_r && !r_prev) r.send; + endrule + + (* no_implicit_conditions *) + rule handle_state_update; + q <= !r && (q || s); + endrule + + method Action set; s.send(); endmethod + method Action reset; r.send(); endmethod + method Action posedge_set; edge_s.send(); endmethod + method Action posedge_reset; edge_r.send(); endmethod + method Bool state = q; + +endmodule + + +endpackage diff --git a/src/peripherals/uart/Uart_bs.bsv b/src/peripherals/uart/Uart_bs.bsv new file mode 100644 index 0000000..0d7a27b --- /dev/null +++ b/src/peripherals/uart/Uart_bs.bsv @@ -0,0 +1,156 @@ + +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + +Description: Bluespec UART with an AXI interface. +*/ +package Uart_bs; + + `define Depth 16 + + import defined_types::*; + import AXI4_Lite_Types::*; + import AXI4_Lite_Fabric::*; + import Semi_FIFOF::*; + `include "instance_defines.bsv" + import RS232_modified::*; + import GetPut::*; + import FIFO::*; + import Clocks::*; + + + interface Ifc_Uart_bs; + interface AXI4_Lite_Slave_IFC#(`PADDR,`Reg_width,`USERSPACE) slave_axi_uart; + interface RS232 coe_rs232; + endinterface + + (*synthesize*) + module mkUart_bs#(Clock core_clock, Reset core_reset)(Ifc_Uart_bs); + + Clock uart_clock<-exposeCurrentClock; + Reset uart_reset<-exposeCurrentReset; + Reg#(Bit#(16)) baud_value <-mkReg(`BAUD_RATE); + UART#(`Depth) uart <-mkUART(8,NONE,STOP_1,baud_value); // charasize,Parity,Stop Bits,BaudDIV + AXI4_Lite_Slave_Xactor_IFC #(`PADDR,`Reg_width,`USERSPACE) s_xactor <- mkAXI4_Lite_Slave_Xactor(clocked_by core_clock, reset_by core_reset); + Reg#(Bit#(4)) rg_status <-mkReg(0); //This register keeps track of whether some data + //is pending to be sent out through the UART Tx + + SyncFIFOIfc#(AXI4_Lite_Rd_Addr #(`PADDR,`USERSPACE)) ff_rd_addr <- mkSyncFIFOToCC(1,core_clock,core_reset); + SyncFIFOIfc#(AXI4_Lite_Wr_Addr #(`PADDR, `USERSPACE)) ff_wr_addr <- mkSyncFIFOToCC(1,core_clock,core_reset); + SyncFIFOIfc#(AXI4_Lite_Wr_Data #(`Reg_width)) ff_wr_data <- mkSyncFIFOToCC(1,core_clock,core_reset); + + SyncFIFOIfc#(AXI4_Lite_Rd_Data #(`Reg_width,`USERSPACE)) ff_rd_resp <- mkSyncFIFOFromCC(1,core_clock); + SyncFIFOIfc#(AXI4_Lite_Wr_Resp #(`USERSPACE)) ff_wr_resp <- mkSyncFIFOFromCC(1,core_clock); + + rule capture_read_request; + let req <- pop_o (s_xactor.o_rd_addr); + ff_rd_addr.enq(req); + endrule + + //Address 'h11304 is uart read data + rule rl_handle_axi4_uart_read(ff_rd_addr.notEmpty && ff_rd_addr.first.araddr[3:2]=='d1); + let req = ff_rd_addr.first; + ff_rd_addr.deq; + `ifdef verbose $display($time,"\tReq: RD_ADDR %h", req.araddr); `endif + Bit#(8) data<-uart.tx.get; + let lv_resp= AXI4_Lite_Rd_Data {rresp:AXI4_LITE_OKAY, rdata: zeroExtend(data), ruser: ?}; + + `ifdef verbose $display($time,"\tResp: RD_RESP %h", req.araddr); `endif + ff_rd_resp.enq(lv_resp); + endrule + + //Address 'b11308 is uart read status + rule rl_handle_axi4_uart_status(ff_rd_addr.notEmpty && ff_rd_addr.first.araddr[3:2]!='d1); + let req =ff_rd_addr.first; + ff_rd_addr.deq; + `ifdef verbose $display($time,"\tReq: RD_ADDR %h", req.araddr); `endif + let lv_resp= AXI4_Lite_Rd_Data {rresp:AXI4_LITE_OKAY, rdata: zeroExtend(rg_status), ruser: ?}; + if(req.araddr[3:2]==2) + lv_resp.rdata=zeroExtend(rg_status); + else if(req.araddr[3:2]==3) + lv_resp.rdata=zeroExtend(baud_value); + else + lv_resp.rresp=AXI4_LITE_SLVERR; + + `ifdef verbose $display($time,"\tResp: RD_RESP %h Status: %b", req.araddr, rg_status); `endif + ff_rd_resp.enq(lv_resp); + endrule + + rule send_read_respone_to_bus; + s_xactor.i_rd_data.enq(ff_rd_resp.first); + ff_rd_resp.deq; + endrule + + rule capture_write_request; + let req <- pop_o (s_xactor.o_wr_addr); + let wr_data <- pop_o(s_xactor.o_wr_data); + ff_wr_addr.enq(req); + ff_wr_data.enq(wr_data); + endrule + + //Address 'b0000 is uart write data + rule rl_handle_axi4_write_rx(ff_wr_addr.notEmpty && ff_wr_data.notEmpty && ff_wr_addr.first.awaddr[3:2]==0); + let wr_addr = ff_wr_addr.first; + ff_wr_addr.deq; + let wr_data = ff_wr_data.first; + ff_wr_data.deq; + + `ifdef verbose $display($time,"\tReq: WR_ADDR %h", wr_addr.awaddr); `endif + `ifdef verbose $display($time,"\tReq: WR_DATA %h", wr_data.wdata); `endif + + uart.rx.put(truncate(wr_data.wdata)); + let lv_resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_OKAY, buser: ?}; + ff_wr_resp.enq(lv_resp); + endrule + + rule rl_handle_axi4_write(ff_wr_addr.notEmpty && ff_wr_data.notEmpty && ff_wr_addr.first.awaddr[3:2]!=0); + let wr_addr = ff_wr_addr.first; + ff_wr_addr.deq; + let wr_data = ff_wr_data.first; + ff_wr_data.deq; + + `ifdef verbose $display($time,"\tReq: WR_ADDR %h", wr_addr.awaddr); `endif + `ifdef verbose $display($time,"\tReq: WR_DATA %h", wr_data.wdata); `endif + + if(wr_addr.awaddr[3:2]=='d3) begin // change the baud value + baud_value<=truncate(wr_data.wdata); + let lv_resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_OKAY, buser: ?}; + ff_wr_resp.enq(lv_resp); + end + else begin + let lv_resp = AXI4_Lite_Wr_Resp {bresp: AXI4_LITE_SLVERR, buser: ?}; + ff_wr_resp.enq(lv_resp); + end + endrule + + rule send_write_response; + s_xactor.i_wr_resp.enq(ff_wr_resp.first); + ff_wr_resp.deq; + endrule + + //The status register is 1 if the transmission FIFO is empty + (*no_implicit_conditions, fire_when_enabled*) + rule rl_update_status_reg; + let lv_status= {pack(uart.receiver_not_empty), pack(uart.receiver_not_full), pack(uart.transmittor_not_empty), pack(uart.transmission_done)}; + rg_status<= lv_status; + `ifdef verbose + if(lv_status==0) + $display($time,"-------UART1 TX Fifo not empty"); + `endif + endrule + + interface slave_axi_uart = s_xactor.axi_side; + interface coe_rs232= uart.rs232; + endmodule +endpackage + diff --git a/src/uncore/axi4/AXI4_Fabric.bsv b/src/uncore/axi4/AXI4_Fabric.bsv new file mode 100644 index 0000000..ca649b1 --- /dev/null +++ b/src/uncore/axi4/AXI4_Fabric.bsv @@ -0,0 +1,323 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ +// Copyright (c) 2013-2017 Bluespec, Inc. All Rights Reserved + +package AXI4_Fabric; + +// ================================================================ +// This package defines a fabric connecting CPUs, Memories and DMAs +// and other IP blocks. + +// ================================================================ +// Bluespec library imports + +import Vector :: *; +import FIFOF :: *; +import ConfigReg :: *; + +// ---------------- +// BSV additional libs + +import Cur_Cycle :: *; + +// ================================================================ +// Project imports + +import Semi_FIFOF :: *; +import AXI4_Types :: *; + +// ================================================================ +// The interface for the fabric module + +interface AXI4_Fabric_IFC #(numeric type num_masters, + numeric type num_slaves, + numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + method Action reset; + method Action set_verbosity (Bit #(4) verbosity); + + // From masters + interface Vector #(num_masters, AXI4_Slave_IFC #(wd_addr, wd_data, wd_user)) v_from_masters; + + // To slaves + interface Vector #(num_slaves, AXI4_Master_IFC #(wd_addr, wd_data, wd_user)) v_to_slaves; +endinterface + +// ================================================================ +// The Fabric module +// The function parameter is an address-decode function, which returns +// returns (True, slave-port-num) if address is mapped to slave-port-num +// (False, ?) if address is unmapped to any port + +module mkAXI4_Fabric #(function Tuple2 #(Bool, Bit #(TLog #(num_slaves))) + fn_addr_to_slave_num (Bit #(wd_addr) addr)) + (AXI4_Fabric_IFC #(num_masters, num_slaves, wd_addr, wd_data, wd_user)) + + provisos (Log #(num_masters, log_nm), + Log #(num_slaves, log_ns), + Log #(TAdd #(num_masters, 1), log_nm_plus_1), + Log #(TAdd #(num_slaves, 1), log_ns_plus_1), + Add #(_dummy, TLog #(num_slaves), log_ns_plus_1)); + + + // Transactors facing masters + Vector #(num_masters, AXI4_Slave_Xactor_IFC #(wd_addr, wd_data, wd_user)) + xactors_from_masters <- replicateM (mkAXI4_Slave_Xactor); + + // Transactors facing slaves + Vector #(num_slaves, AXI4_Master_Xactor_IFC #(wd_addr, wd_data, wd_user)) + xactors_to_slaves <- replicateM (mkAXI4_Master_Xactor); + + // FIFOs to keep track of which master originated a transaction, in + // order to route corresponding responses back to that master. + // Legal masters are 0..(num_masters-1) + // The value of 'num_masters' is used for decode errors (no such slave) + + Vector #(num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_wr_sjs <- replicateM (mkFIFOF); + Vector #(num_masters, FIFOF #(Bit #(wd_user))) v_f_wr_err_user <- replicateM (mkFIFOF); + Vector #(num_slaves, FIFOF #(Bit #(log_nm_plus_1))) v_f_wr_mis <- replicateM (mkFIFOF); + + Vector #(num_masters, FIFOF #(Bit #(log_ns_plus_1))) v_f_rd_sjs <- replicateM (mkFIFOF); + Vector #(num_masters, FIFOF #(Bit #(wd_user))) v_f_rd_err_user <- replicateM (mkFIFOF); + Vector #(num_slaves, FIFOF #(Bit #(log_nm_plus_1))) v_f_rd_mis <- replicateM (mkFIFOF); + + // ---------------------------------------------------------------- + // BEHAVIOR + + function Bool wr_move_from_mi_to_sj (Integer mi, Integer sj); + let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr; + match { .legal, .slave_num } = fn_addr_to_slave_num (addr); + return (legal && (slave_num == fromInteger (sj))); + endfunction + + function Bool wr_illegal_sj (Integer mi); + let addr = xactors_from_masters [mi].o_wr_addr.first.awaddr; + match { .legal, ._ } = fn_addr_to_slave_num (addr); + return (! legal); + endfunction + + function Bool rd_move_from_mi_to_sj (Integer mi, Integer sj); + let addr = xactors_from_masters [mi].o_rd_addr.first.araddr; + match { .legal, .slave_num } = fn_addr_to_slave_num (addr); + return (legal && (slave_num == fromInteger (sj))); + endfunction + + function Bool rd_illegal_sj (Integer mi); + let addr = xactors_from_masters [mi].o_rd_addr.first.araddr; + match { .legal, ._ } = fn_addr_to_slave_num (addr); + return (! legal); + endfunction + + // ---------------- + // Wr requests from masters to slaves + + // Legal destination slaves + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) + + rule rl_wr_xaction_master_to_slave (wr_move_from_mi_to_sj (mi, sj)); + AXI4_Wr_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr); + AXI4_Wr_Data #(wd_data) d <- pop_o (xactors_from_masters [mi].o_wr_data); + + xactors_to_slaves [sj].i_wr_addr.enq (a); + xactors_to_slaves [sj].i_wr_data.enq (d); + + v_f_wr_mis [sj].enq (fromInteger (mi)); + v_f_wr_sjs [mi].enq (fromInteger (sj)); + + `ifdef verbose + $display ($time,"\tAXI4_Fabric: wr master [%0d] -> slave [%0d]", mi, sj); + $display ($time,"\t ", fshow (a)); + $display ($time,"\t ", fshow (d)); + `endif + endrule + + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) + + rule rl_wr_xaction_master_to_slave_data ((v_f_wr_mis [sj].first == fromInteger (mi)) + && (v_f_wr_sjs [mi].first == fromInteger (sj))); + AXI4_Wr_Data #(wd_data) d <- pop_o (xactors_from_masters [mi].o_wr_data); + xactors_to_slaves [sj].i_wr_data.enq(d); + `ifdef verbose $display ($time,"\tAXI4_Fabric: Write Data -> slave[%0d] \n",sj,$time,"\t", fshow (d)); `endif + endrule + + + // Non-existent destination slaves + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + rule rl_wr_xaction_no_such_slave (wr_illegal_sj (mi)); + AXI4_Wr_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_wr_addr); + AXI4_Wr_Data #(wd_data) d <- pop_o (xactors_from_masters [mi].o_wr_data); + + v_f_wr_sjs [mi].enq (fromInteger (valueOf (num_slaves))); + v_f_wr_err_user [mi].enq (a.awuser); + `ifdef verbose + $display ($time,"\tAXI4_Fabric: wr master [%0d] -> illegal addr", mi); + $display ($time,"\t ", fshow (a)); + `endif + endrule + + // ---------------- + // Rd requests from masters to slaves + + // Legal destination slaves + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) + + rule rl_rd_xaction_master_to_slave (rd_move_from_mi_to_sj (mi, sj)); + AXI4_Rd_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr); + + xactors_to_slaves [sj].i_rd_addr.enq (a); + + v_f_rd_mis [sj].enq (fromInteger (mi)); + v_f_rd_sjs [mi].enq (fromInteger (sj)); + + `ifdef verbose + $display ($time,"\tAXI4_Fabric: rd master [%0d] -> slave [%0d]", mi, sj); + $display ($time,"\t ", fshow (a)); + `endif + endrule + + // Non-existent destination slaves + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + rule rl_rd_xaction_no_such_slave (rd_illegal_sj (mi)); + AXI4_Rd_Addr #(wd_addr, wd_user) a <- pop_o (xactors_from_masters [mi].o_rd_addr); + + v_f_rd_sjs [mi].enq (fromInteger (valueOf (num_slaves))); + v_f_rd_err_user [mi].enq (a.aruser); + + `ifdef verbose + $display ($time,"\tAXI4_Fabric: rd master [%0d] -> illegal addr", mi); + $display ($time,"\t ", fshow (a)); + `endif + endrule + + // ---------------- + // Wr responses from slaves to masters + + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) + + rule rl_wr_resp_slave_to_master ( (v_f_wr_mis [sj].first == fromInteger (mi)) + && (v_f_wr_sjs [mi].first == fromInteger (sj))); + v_f_wr_mis [sj].deq; + v_f_wr_sjs [mi].deq; + AXI4_Wr_Resp #(wd_user) b <- pop_o (xactors_to_slaves [sj].o_wr_resp); + + xactors_from_masters [mi].i_wr_resp.enq (b); + + `ifdef verbose + $display ($time,"\tAXI4_Fabric: wr master [%0d] <- slave [%0d]", mi, sj); + $display ($time,"\t ", fshow (b)); + `endif + endrule + + // ---------------- + // Wr error responses to masters + // v_f_wr_sjs [mi].first has value num_slaves (illegal value) + // v_f_wr_err_user [mi].first contains the request's 'user' data + + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + + rule rl_wr_resp_err_to_master (v_f_wr_sjs [mi].first == fromInteger (valueOf (num_slaves))); + v_f_wr_sjs [mi].deq; + v_f_wr_err_user [mi].deq; + + //let b = AXI4_Wr_Resp {bresp: AXI4_DECERR, buser: v_f_wr_err_user [mi].first}; + let b = AXI4_Wr_Resp {bresp: AXI4_DECERR, buser: v_f_wr_err_user [mi].first, bid:fromInteger(mi)}; + + xactors_from_masters [mi].i_wr_resp.enq (b); + + `ifdef verbose + $display ($time,"\tAXI4_Fabric: wr master [%0d] <- error", mi); + $display ($time,"\t ", fshow (b)); + `endif + endrule + + // ---------------- + // Rd responses from slaves to masters + + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) + + rule rl_rd_resp_slave_to_master ( (v_f_rd_mis [sj].first == fromInteger (mi)) + && (v_f_rd_sjs [mi].first == fromInteger (sj))); + AXI4_Rd_Data #(wd_data, wd_user) r <- pop_o (xactors_to_slaves [sj].o_rd_data); + + xactors_from_masters [mi].i_rd_data.enq (r); + if(r.rlast)begin + v_f_rd_mis [sj].deq; + v_f_rd_sjs [mi].deq; + end + + `ifdef verbose + $display ($time,"\tAXI4_Fabric: rd master [%0d] <- slave [%0d]", mi, sj); + $display ($time,"\t ", fshow (r)); + `endif + endrule + + // ---------------- + // Rd error responses to masters + // v_f_rd_sjs [mi].first has value num_slaves (illegal value) + // v_f_rd_err_user [mi].first contains the request's 'user' data + + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) + + rule rl_rd_resp_err_to_master (v_f_rd_sjs [mi].first == fromInteger (valueOf (num_slaves))); + v_f_rd_sjs [mi].deq; + v_f_rd_err_user [mi].deq; + + Bit #(wd_data) data = 0; + let r = AXI4_Rd_Data {rresp: AXI4_DECERR, ruser: v_f_rd_err_user [mi].first, rdata: data, rlast:True,rid:fromInteger(mi)}; + + xactors_from_masters [mi].i_rd_data.enq (r); + + `ifdef verbose + $display ($time,"\tAXI4_Fabric: rd master [%0d] <- error", mi); + $display ($time,"\t ", fshow (r)); + `endif + endrule + + // ---------------------------------------------------------------- + // INTERFACE + + function AXI4_Slave_IFC #(wd_addr, wd_data, wd_user) f1 (Integer j) = xactors_from_masters [j].axi_side; + function AXI4_Master_IFC #(wd_addr, wd_data, wd_user) f2 (Integer j) = xactors_to_slaves [j].axi_side; + + method Action reset; + for (Integer mi = 0; mi < valueOf (num_masters); mi = mi + 1) begin + xactors_from_masters [mi].reset; + + v_f_wr_sjs [mi].clear; + v_f_wr_err_user [mi].clear; + + v_f_rd_sjs [mi].clear; + v_f_rd_err_user [mi].clear; + end + + for (Integer sj = 0; sj < valueOf (num_slaves); sj = sj + 1) begin + xactors_to_slaves [sj].reset; + v_f_wr_mis [sj].clear; + v_f_rd_mis [sj].clear; + end + endmethod + + interface v_from_masters = genWith (f1); + interface v_to_slaves = genWith (f2); +endmodule + +// ================================================================ + +endpackage: AXI4_Fabric diff --git a/src/uncore/axi4/AXI4_Types.bsv b/src/uncore/axi4/AXI4_Types.bsv new file mode 100644 index 0000000..eb17c8d --- /dev/null +++ b/src/uncore/axi4/AXI4_Types.bsv @@ -0,0 +1,658 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ +// Copyright (c) 2017 Bluespec, Inc. All Rights Reserved + +package AXI4_Types; + +// ================================================================ +// Facilities for ARM AXI4-Lite, consisting of 5 independent channels: +// Write Address, Write Data, Write Response, Read Address and Read Data + +// Ref: ARM document: +// AMBA AXI and ACE Protocol Specification +// AXI3, AXI4, and AXI4-Lite +// ACE and ACE-Lite +// ARM IHI 0022E (ID022613) +// Issue E, 22 Feb 2013 + +// See export list below + +// ================================================================ +// Exports + +export + +// RTL-level interfaces (signals/buses) +AXI4_Master_IFC (..), +AXI4_Slave_IFC (..), + +// Dummy slave that accepts no requests and generates no response +// Used for tying-off unused slave interfaces on fabrics. +dummy_AXI4_Slave_ifc, + +// Higher-level enums and structs for the 5 AXI4 channel payloads +AXI4_Resp (..), + +AXI4_Wr_Addr (..), +AXI4_Wr_Data (..), +AXI4_Wr_Resp (..), +AXI4_Rd_Addr (..), +AXI4_Rd_Data (..), + +// Higher-level FIFO-like interfaces for the 5 AXI4 channels, +AXI4_Master_Xactor_IFC (..), +AXI4_Slave_Xactor_IFC (..), + +// Transactors from RTL-level interfacecs to FIFO-like interfaces. +mkAXI4_Master_Xactor, +mkAXI4_Slave_Xactor; + +// ================================================================ +// BSV library imports + +import FIFOF :: *; +import Connectable :: *; + +// ---------------- +// BSV additional libs + +import Semi_FIFOF :: *; + +// **************************************************************** +// **************************************************************** +// Section: RTL-level interfaces +// **************************************************************** +// **************************************************************** + +// ================================================================ +// These are the signal-level interfaces for an AXI4-Lite master. +// The (*..*) attributes ensure that when bsc compiles this to Verilog, +// we get exactly the signals specified in the ARM spec. + +interface AXI4_Master_IFC #(numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + // Wr Addr channel + (* always_ready, result="awvalid" *) method Bool m_awvalid; // out + (* always_ready, result="awaddr" *) method Bit #(wd_addr) m_awaddr; // out + (* always_ready, result="awuser" *) method Bit #(wd_user) m_awuser; // out + (* always_ready, result="awlen" *) method Bit #(8) m_awlen; // out + (* always_ready, result="awsize" *) method Bit #(3) m_awsize; // out + (* always_ready, result="awburst" *) method Bit #(2) m_awburst; // out + (* always_ready, result="awid" *) method Bit #(4) m_awid; // out + (* always_ready, always_enabled *) method Action m_awready ((* port="awready" *) Bool awready); // in + + // Wr Data channel + (* always_ready, result="wvalid" *) method Bool m_wvalid; // out + (* always_ready, result="wdata" *) method Bit #(wd_data) m_wdata; // out + (* always_ready, result="wstrb" *) method Bit #(TDiv #(wd_data, 8)) m_wstrb; // out + (* always_ready, result="wlast" *) method Bool m_wlast; // out + (* always_ready, result="wid" *) method Bit #(4) m_wid; // out + (* always_ready, always_enabled *) method Action m_wready ((* port="wready" *) Bool wready); // in + + // Wr Response channel + (* always_ready, always_enabled *) + method Action m_bvalid ((* port="bvalid" *) Bool bvalid, // in + (* port="bresp" *) Bit #(2) bresp, // in + (* port="buser" *) Bit #(wd_user) buser, // in + (* port="bid"*) Bit#(4) bid); // in + (* always_ready, result="bready" *) + method Bool m_bready; // out + + // Rd Addr channel + (* always_ready, result="arvalid" *) method Bool m_arvalid; // out + (* always_ready, result="araddr" *) method Bit #(wd_addr) m_araddr; // out + (* always_ready, result="aruser" *) method Bit #(wd_user) m_aruser; // out + (* always_ready, result="arlen" *) method Bit #(8) m_arlen; // out + (* always_ready, result="arsize" *) method Bit #(3) m_arsize; // out + (* always_ready, result="arburst" *) method Bit #(2) m_arburst; // out + (* always_ready, result="arid" *) method Bit #(4) m_arid; // out + (* always_ready, always_enabled *) method Action m_arready ((* port="arready" *) Bool arready); // in + + // Rd Data channel + (* always_ready, always_enabled *) + method Action m_rvalid ((* port="rvalid" *) Bool rvalid, // in + (* port="rresp" *) Bit #(2) rresp, // in + (* port="rdata" *) Bit #(wd_data) rdata, // in + (* port="rlast" *) Bool rlast, // in + (* port="ruser" *) Bit #(wd_user) ruser, // in + (* port="rid" *) Bit #(4) rid); // in + (* always_ready, result="rready" *) + method Bool m_rready; // out +endinterface: AXI4_Master_IFC + +// ================================================================ +// These are the signal-level interfaces for an AXI4-Lite slave. +// The (*..*) attributes ensure that when bsc compiles this to Verilog, +// we get exactly the signals specified in the ARM spec. + +interface AXI4_Slave_IFC #(numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + // Wr Addr channel + (* always_ready, always_enabled *) + method Action m_awvalid ((* port="awvalid" *) Bool awvalid, // in + (* port="awaddr" *) Bit #(wd_addr) awaddr, // in + (* port="awsize" *) Bit #(3) awsize, // in + (* port="awuser" *) Bit #(wd_user) awuser, // in + (* port="awlen" *) Bit #(8) awlen, // in + (* port="awburst" *) Bit #(2) awburst, // in + (* port="awid" *) Bit#(4) awid); + (* always_ready, result="awready" *) + method Bool m_awready; // out + + // Wr Data channel + (* always_ready, always_enabled *) + method Action m_wvalid ((* port="wvalid" *) Bool wvalid, // in + (* port="wdata" *) Bit #(wd_data) wdata, // in + (* port="wstrb" *) Bit #(TDiv #(wd_data,8)) wstrb, // in + (* port="wlast" *) Bool wlast, + (* port="wid" *) Bit#(4) wid); + (* always_ready, result="wready" *) + method Bool m_wready; // out + + // Wr Response channel + (* always_ready, result="bvalid" *) method Bool m_bvalid; // out + (* always_ready, result="bresp" *) method Bit #(2) m_bresp; // out + (* always_ready, result="buser" *) method Bit #(wd_user) m_buser; // out + (* always_ready, result="bid" *) method Bit #(4) m_bid; // out + (* always_ready, always_enabled *) method Action m_bready ((* port="bready" *) Bool bready); // in + + // Rd Addr channel + (* always_ready, always_enabled *) + method Action m_arvalid ((* port="arvalid" *) Bool arvalid, // in + (* port="araddr" *) Bit #(wd_addr) araddr, // in + (* port="arsize" *) Bit #(3) arsize, // in + (* port="aruser" *) Bit #(wd_user) aruser, // in + (* port="arlen" *) Bit #(8) arlen, // in + (* port="arburst" *) Bit #(2) arburst, // in + (* port="arid" *) Bit#(4) arid + ); + (* always_ready, result="arready" *) + method Bool m_arready; // out + + // Rd Data channel + (* always_ready, result="rvalid" *) method Bool m_rvalid; // out + (* always_ready, result="rresp" *) method Bit #(2) m_rresp; // out + (* always_ready, result="rdata" *) method Bit #(wd_data) m_rdata; // out + (* always_ready, result="rlast" *) method Bool m_rlast; // out + (* always_ready, result="ruser" *) method Bit #(wd_user) m_ruser; // out + (* always_ready, result="rid" *) method Bit #(4) m_rid; // out + (* always_ready, always_enabled *) method Action m_rready ((* port="rready" *) Bool rready); // in +endinterface: AXI4_Slave_IFC + +// ================================================================ +// Connecting signal-level interfaces + +instance Connectable #(AXI4_Master_IFC #(wd_addr, wd_data, wd_user), + AXI4_Slave_IFC #(wd_addr, wd_data, wd_user)); + + module mkConnection #(AXI4_Master_IFC #(wd_addr, wd_data, wd_user) axim, + AXI4_Slave_IFC #(wd_addr, wd_data, wd_user) axis) + (Empty); + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_wr_addr_channel; + axis.m_awvalid (axim.m_awvalid, axim.m_awaddr, axim.m_awsize, axim.m_awuser, axim.m_awlen, axim.m_awburst, axim.m_awid); + axim.m_awready (axis.m_awready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_wr_data_channel; + axis.m_wvalid (axim.m_wvalid, axim.m_wdata, axim.m_wstrb, axim.m_wlast, axim.m_wid); + axim.m_wready (axis.m_wready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_wr_response_channel; + axim.m_bvalid (axis.m_bvalid, axis.m_bresp, axis.m_buser, axis.m_bid); + axis.m_bready (axim.m_bready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_rd_addr_channel; + axis.m_arvalid (axim.m_arvalid, axim.m_araddr, axim.m_arsize, axim.m_aruser, axim.m_arlen, axim.m_arburst, axim.m_arid); + axim.m_arready (axis.m_arready); + endrule + + (* fire_when_enabled, no_implicit_conditions *) + rule rl_rd_data_channel; + axim.m_rvalid (axis.m_rvalid, axis.m_rresp, axis.m_rdata, axis.m_rlast, axis.m_ruser, axis.m_rid); + axis.m_rready (axim.m_rready); + endrule + endmodule +endinstance + +// ================================================================ +// AXI4-Lite dummy slave: never accepts requests, never produces responses + +AXI4_Slave_IFC #(wd_addr, wd_data, wd_user) + dummy_AXI4_Slave_ifc = interface AXI4_Slave_IFC + // Wr Addr channel + method Action m_awvalid (Bool awvalid, + Bit #(wd_addr) awaddr, + Bit #(3) awsize, + Bit #(wd_user) awuser, + Bit #(8) awlen, + Bit #(2) awburst, + Bit #(4) awid); + noAction; + endmethod + + method Bool m_awready; + return False; + endmethod + + // Wr Data channel + method Action m_wvalid (Bool wvalid, + Bit #(wd_data) wdata, + Bit #(TDiv #(wd_data,8)) wstrb, + Bool wlast, + Bit#(4) wid); + noAction; + endmethod + + method Bool m_wready; + return False; + endmethod + + // Wr Response channel + method Bool m_bvalid; + return False; + endmethod + + method Bit #(2) m_bresp; + return 0; + endmethod + + method Bit #(wd_user) m_buser; + return ?; + endmethod + method Bit #(4) m_bid; + return ?; + endmethod + + method Action m_bready (Bool bready); + noAction; + endmethod + + // Rd Addr channel + method Action m_arvalid (Bool arvalid, + Bit #(wd_addr) araddr, + Bit#(3) arsize, + Bit #(wd_user) aruser, + Bit#(8) arlen, + Bit#(2) arburst, + Bit#(4) arid); + noAction; + endmethod + + method Bool m_arready; + return False; + endmethod + + // Rd Data channel + method Bool m_rvalid; + return False; + endmethod + + method Bit #(2) m_rresp; + return 0; + endmethod + + method Bit #(wd_data) m_rdata; + return 0; + endmethod + method Bool m_rlast; + return ?; + endmethod + + method Bit #(wd_user) m_ruser; + return ?; + endmethod + + method Action m_rready (Bool rready); + noAction; + endmethod + endinterface; + +// **************************************************************** +// **************************************************************** +// Section: Higher-level FIFO-like interfaces and transactors +// **************************************************************** +// **************************************************************** + +// ================================================================ +// Higher-level types for payloads (rather than just bits) + +typedef enum { AXI4_OKAY, AXI4_EXOKAY, AXI4_SLVERR, AXI4_DECERR } AXI4_Resp +deriving (Bits, Eq, FShow); + +// Write Address channel + +typedef struct { + Bit #(wd_addr) awaddr; + Bit #(wd_user) awuser; + Bit#(8) awlen; + Bit#(3) awsize; + Bit#(2) awburst; + Bit#(4) awid; + } AXI4_Wr_Addr #(numeric type wd_addr, numeric type wd_user) +deriving (Bits, FShow); + +// Write Data channel + +typedef struct { + Bit #(wd_data) wdata; + Bit #(TDiv #(wd_data, 8)) wstrb; + Bit#(4) wid; + Bool wlast; + } AXI4_Wr_Data #(numeric type wd_data) +deriving (Bits, FShow); + +// Write Response channel + +typedef struct { + AXI4_Resp bresp; + Bit #(wd_user) buser; + Bit#(4) bid; + } AXI4_Wr_Resp #(numeric type wd_user) +deriving (Bits, FShow); + +// Read Address channel + +typedef struct { + Bit #(wd_addr) araddr; + Bit #(wd_user) aruser; + Bit#(3) arsize; + Bit#(8) arlen; + Bit#(2) arburst; + Bit#(4) arid; + } AXI4_Rd_Addr #(numeric type wd_addr, numeric type wd_user) +deriving (Bits, FShow); + +// Read Data channel + +typedef struct { + AXI4_Resp rresp; + Bit #(wd_data) rdata; + Bool rlast; + Bit #(wd_user) ruser; + Bit#(4) rid; + } AXI4_Rd_Data #(numeric type wd_data, numeric type wd_user) +deriving (Bits, FShow); + +// ================================================================ +// Master transactor interface + +interface AXI4_Master_Xactor_IFC #(numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + method Action reset; + + // AXI side + interface AXI4_Master_IFC #(wd_addr, wd_data, wd_user) axi_side; + + // FIFOF side + interface FIFOF_I #(AXI4_Wr_Addr #(wd_addr, wd_user)) i_wr_addr; + interface FIFOF_I #(AXI4_Wr_Data #(wd_data)) i_wr_data; + interface FIFOF_O #(AXI4_Wr_Resp #(wd_user)) o_wr_resp; + + interface FIFOF_I #(AXI4_Rd_Addr #(wd_addr, wd_user)) i_rd_addr; + interface FIFOF_O #(AXI4_Rd_Data #(wd_data, wd_user)) o_rd_data; +endinterface: AXI4_Master_Xactor_IFC + +// ---------------------------------------------------------------- +// Master transactor + +module mkAXI4_Master_Xactor (AXI4_Master_Xactor_IFC #(wd_addr, wd_data, wd_user)); + + Bool unguarded = True; + Bool guarded = False; + + // These FIFOs are guarded on BSV side, unguarded on AXI side + FIFOF #(AXI4_Wr_Addr #(wd_addr, wd_user)) f_wr_addr <- mkGFIFOF (guarded, unguarded); + FIFOF #(AXI4_Wr_Data #(wd_data)) f_wr_data <- mkGFIFOF (guarded, unguarded); + FIFOF #(AXI4_Wr_Resp #(wd_user)) f_wr_resp <- mkGFIFOF (unguarded, guarded); + + FIFOF #(AXI4_Rd_Addr #(wd_addr, wd_user)) f_rd_addr <- mkGFIFOF (guarded, unguarded); + FIFOF #(AXI4_Rd_Data #(wd_data, wd_user)) f_rd_data <- mkGFIFOF (unguarded, guarded); + + // ---------------------------------------------------------------- + // INTERFACE + + method Action reset; + f_wr_addr.clear; + f_wr_data.clear; + f_wr_resp.clear; + f_rd_addr.clear; + f_rd_data.clear; + endmethod + + // AXI side + interface axi_side = interface AXI4_Master_IFC; + // Wr Addr channel + method Bool m_awvalid = f_wr_addr.notEmpty; + method Bit #(wd_addr) m_awaddr = f_wr_addr.first.awaddr; + method Bit #(wd_user) m_awuser = f_wr_addr.first.awuser; + method Bit #(8) m_awlen = f_wr_addr.first.awlen; + method Bit #(3) m_awsize = f_wr_addr.first.awsize; + method Bit #(2) m_awburst = f_wr_addr.first.awburst; + method Bit #(4) m_awid = f_wr_addr.first.awid; + method Action m_awready (Bool awready); + if (f_wr_addr.notEmpty && awready) f_wr_addr.deq; + endmethod + + // Wr Data channel + method Bool m_wvalid = f_wr_data.notEmpty; + method Bit #(wd_data) m_wdata = f_wr_data.first.wdata; + method Bit #(TDiv #(wd_data, 8)) m_wstrb = f_wr_data.first.wstrb; + method Bool m_wlast = f_wr_data.first.wlast; + method Bit#(4) m_wid = f_wr_data.first.wid; + method Action m_wready (Bool wready); + if (f_wr_data.notEmpty && wready) f_wr_data.deq; + endmethod + + // Wr Response channel + method Action m_bvalid (Bool bvalid, Bit #(2) bresp, Bit #(wd_user) buser, Bit#(4) bid); + if (bvalid && f_wr_resp.notFull) + f_wr_resp.enq (AXI4_Wr_Resp {bresp: unpack (bresp), buser: buser, bid:bid}); + endmethod + + method Bool m_bready; + return f_wr_resp.notFull; + endmethod + + // Rd Addr channel + method Bool m_arvalid = f_rd_addr.notEmpty; + method Bit #(wd_addr) m_araddr = f_rd_addr.first.araddr; + method Bit #(wd_user) m_aruser = f_rd_addr.first.aruser; + method Bit #(3) m_arsize = f_rd_addr.first.arsize; + method Bit #(8) m_arlen = f_rd_addr.first.arlen; + method Bit #(2) m_arburst = f_rd_addr.first.arburst; + method Bit #(4) m_arid = f_rd_addr.first.arid; + method Action m_arready (Bool arready); + if (f_rd_addr.notEmpty && arready) f_rd_addr.deq; + endmethod + + // Rd Data channel + method Action m_rvalid (Bool rvalid, + Bit #(2) rresp, + Bit #(wd_data) rdata, + Bool rlast, + Bit #(wd_user) ruser, + Bit#(4) rid); + if (rvalid && f_rd_data.notFull) + f_rd_data.enq (AXI4_Rd_Data {rresp: unpack (rresp), + rdata: rdata, + rlast: rlast, + ruser: ruser, + rid: rid}); + endmethod + + method Bool m_rready; + return f_rd_data.notFull; + endmethod + + endinterface; + + // FIFOF side + interface i_wr_addr = to_FIFOF_I (f_wr_addr); + interface i_wr_data = to_FIFOF_I (f_wr_data); + interface o_wr_resp = to_FIFOF_O (f_wr_resp); + + interface i_rd_addr = to_FIFOF_I (f_rd_addr); + interface o_rd_data = to_FIFOF_O (f_rd_data); +endmodule: mkAXI4_Master_Xactor + +// ================================================================ +// Slave transactor interface + +interface AXI4_Slave_Xactor_IFC #(numeric type wd_addr, + numeric type wd_data, + numeric type wd_user); + method Action reset; + + // AXI side + interface AXI4_Slave_IFC #(wd_addr, wd_data, wd_user) axi_side; + + // FIFOF side + interface FIFOF_O #(AXI4_Wr_Addr #(wd_addr, wd_user)) o_wr_addr; + interface FIFOF_O #(AXI4_Wr_Data #(wd_data)) o_wr_data; + interface FIFOF_I #(AXI4_Wr_Resp #(wd_user)) i_wr_resp; + + interface FIFOF_O #(AXI4_Rd_Addr #(wd_addr, wd_user)) o_rd_addr; + interface FIFOF_I #(AXI4_Rd_Data #(wd_data, wd_user)) i_rd_data; +endinterface: AXI4_Slave_Xactor_IFC + +// ---------------------------------------------------------------- +// Slave transactor + +module mkAXI4_Slave_Xactor (AXI4_Slave_Xactor_IFC #(wd_addr, wd_data, wd_user)); + + Bool unguarded = True; + Bool guarded = False; + + // These FIFOs are guarded on BSV side, unguarded on AXI side + FIFOF #(AXI4_Wr_Addr #(wd_addr, wd_user)) f_wr_addr <- mkGFIFOF (unguarded, guarded); + FIFOF #(AXI4_Wr_Data #(wd_data)) f_wr_data <- mkGFIFOF (unguarded, guarded); + FIFOF #(AXI4_Wr_Resp #(wd_user)) f_wr_resp <- mkGFIFOF (guarded, unguarded); + + FIFOF #(AXI4_Rd_Addr #(wd_addr, wd_user)) f_rd_addr <- mkGFIFOF (unguarded, guarded); + FIFOF #(AXI4_Rd_Data #(wd_data, wd_user)) f_rd_data <- mkGFIFOF (guarded, unguarded); + + // ---------------------------------------------------------------- + // INTERFACE + + method Action reset; + f_wr_addr.clear; + f_wr_data.clear; + f_wr_resp.clear; + f_rd_addr.clear; + f_rd_data.clear; + endmethod + + // AXI side + interface axi_side = interface AXI4_Slave_IFC; + // Wr Addr channel + method Action m_awvalid (Bool awvalid, + Bit #(wd_addr) awaddr, + Bit#(3) awsize, + Bit #(wd_user) awuser, + Bit#(8) awlen, + Bit#(2) awburst, + Bit#(4) awid); + if (awvalid && f_wr_addr.notFull) + f_wr_addr.enq (AXI4_Wr_Addr {awaddr: awaddr, + awsize:awsize, + awuser: awuser, + awlen:awlen, + awburst:awburst, + awid:awid}); + endmethod + + method Bool m_awready; + return f_wr_addr.notFull; + endmethod + + // Wr Data channel + method Action m_wvalid (Bool wvalid, + Bit #(wd_data) wdata, + Bit #(TDiv #(wd_data, 8)) wstrb, + Bool wlast, + Bit#(4) wid); + if (wvalid && f_wr_data.notFull) + f_wr_data.enq (AXI4_Wr_Data {wdata: wdata, wstrb: wstrb, wlast:wlast, wid:wid}); + endmethod + + method Bool m_wready; + return f_wr_data.notFull; + endmethod + + // Wr Response channel + method Bool m_bvalid = f_wr_resp.notEmpty; + method Bit #(2) m_bresp = pack (f_wr_resp.first.bresp); + method Bit #(wd_user) m_buser = f_wr_resp.first.buser; + method Bit #(4) m_bid = f_wr_resp.first.bid; + method Action m_bready (Bool bready); + if (bready && f_wr_resp.notEmpty) + f_wr_resp.deq; + endmethod + + // Rd Addr channel + method Action m_arvalid (Bool arvalid, + Bit #(wd_addr) araddr, + Bit#(3) arsize, + Bit #(wd_user) aruser, + Bit#(8) arlen, + Bit#(2) arburst, + Bit#(4) arid); + if (arvalid && f_rd_addr.notFull) + f_rd_addr.enq (AXI4_Rd_Addr {araddr: araddr, + arsize: arsize, + aruser: aruser, + arlen : arlen, + arburst:arburst, + arid:arid}); + endmethod + + method Bool m_arready; + return f_rd_addr.notFull; + endmethod + + // Rd Data channel + method Bool m_rvalid = f_rd_data.notEmpty; + method Bit #(2) m_rresp = pack (f_rd_data.first.rresp); + method Bit #(wd_data) m_rdata = f_rd_data.first.rdata; + method Bool m_rlast = f_rd_data.first.rlast; + method Bit #(wd_user) m_ruser = f_rd_data.first.ruser; + method Bit#(4) m_rid=f_rd_data.first.rid; + method Action m_rready (Bool rready); + if (rready && f_rd_data.notEmpty) + f_rd_data.deq; + endmethod + endinterface; + + // FIFOF side + interface o_wr_addr = to_FIFOF_O (f_wr_addr); + interface o_wr_data = to_FIFOF_O (f_wr_data); + interface i_wr_resp = to_FIFOF_I (f_wr_resp); + + interface o_rd_addr = to_FIFOF_O (f_rd_addr); + interface i_rd_data = to_FIFOF_I (f_rd_data); +endmodule: mkAXI4_Slave_Xactor + +// ================================================================ + +endpackage diff --git a/src/uncore/axi4/Cur_Cycle.bsv b/src/uncore/axi4/Cur_Cycle.bsv new file mode 100644 index 0000000..ac0b600 --- /dev/null +++ b/src/uncore/axi4/Cur_Cycle.bsv @@ -0,0 +1,28 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ +// Copyright (c) 2013-2017 Bluespec, Inc. All Rights Reserved. + +package Cur_Cycle; + +// ================================================================ +// A convenience function to return the current cycle number during BSV simulations + +ActionValue #(Bit #(32)) cur_cycle = actionvalue + Bit #(32) t <- $stime; + return t / 10; + endactionvalue; + +// ================================================================ + +endpackage diff --git a/src/uncore/axi4/Semi_FIFOF.bsv b/src/uncore/axi4/Semi_FIFOF.bsv new file mode 100644 index 0000000..08ff7c1 --- /dev/null +++ b/src/uncore/axi4/Semi_FIFOF.bsv @@ -0,0 +1,129 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +* Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED +TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +----------------------------------------------------------------------- + +*/ +// Copyright (c) 2017 Bluespec, Inc. All Rights Reserved + +package Semi_FIFOF; + +// ================================================================ +// Separate interfaces for input-side and output-side of FIFOF. +// Conversion functions to these, from FIFOF interfaces. + +// ================================================================ +// BSV library imports + +import FIFOF :: *; +import Connectable :: *; + +// ================================================================ +// Semi-FIFOF interfaces + +interface FIFOF_I #(type t); + method Action enq (t x); + method Bool notFull (); +endinterface + +interface FIFOF_O #(type t); + method t first (); + method Action deq (); + method Bool notEmpty (); +endinterface + +// ================================================================ +// Converters from FIFOF + +function FIFOF_I #(t) to_FIFOF_I (FIFOF #(t) f); + return interface FIFOF_I; + method enq (x) = f.enq (x); + method notFull = f.notFull; + endinterface; +endfunction + +function FIFOF_O #(t) to_FIFOF_O (FIFOF #(t) f); + return interface FIFOF_O; + method first = f.first; + method deq = f.deq; + method notEmpty = f.notEmpty; + endinterface; +endfunction + +// ================================================================ +// Connections + +// ---------------- +// FIFOF_O to a FIFOF_I + +instance Connectable #(FIFOF_O #(t), FIFOF_I #(t)); + module mkConnection #(FIFOF_O #(t) fo, FIFOF_I #(t) fi) (Empty); + rule rl_connect; + fi.enq (fo.first); + fo.deq; + endrule + endmodule +endinstance + +// ---------------- +// FIFOF_O to a FIFOF + +instance Connectable #(FIFOF_O #(t), FIFOF #(t)); + module mkConnection #(FIFOF_O #(t) fo, FIFOF #(t) fi) (Empty); + rule rl_connect; + fi.enq (fo.first); + fo.deq; + endrule + endmodule +endinstance + +// ---------------- +// FIFOF to a FIFOF_I + +instance Connectable #(FIFOF #(t), FIFOF_I #(t)); + module mkConnection #(FIFOF #(t) fo, FIFOF_I #(t) fi) (Empty); + rule rl_connect; + fi.enq (fo.first); + fo.deq; + endrule + endmodule +endinstance + +// ================================================================ +// Convenience function combining first/enq + +function ActionValue #(t) pop_o (FIFOF_O #(t) f); + actionvalue + f.deq; + return f.first; + endactionvalue +endfunction + +// ================================================================ + +endpackage diff --git a/src/uncore/axi4lite/AXI4Lite_AXI4_Bridge.bsv b/src/uncore/axi4lite/AXI4Lite_AXI4_Bridge.bsv new file mode 100644 index 0000000..238b60f --- /dev/null +++ b/src/uncore/axi4lite/AXI4Lite_AXI4_Bridge.bsv @@ -0,0 +1,192 @@ +/* +Copyright (c) 2013, IIT Madras +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +* Neither the name of IIT Madras nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +*/ +package AXI4Lite_AXI4_Bridge; + /*=== Project imports ====*/ + import AXI4_Lite_Fabric::*; + import AXI4_Lite_Types::*; + import AXI4_Fabric::*; + import AXI4_Types ::*; + import Semi_FIFOF ::*; + import defined_types::*; + import axi_addr_generator::*; + `include "instance_defines.bsv" + /*======================*/ + /*=== Package imports ===*/ + import Clocks::*; + /*=======================*/ + + interface Ifc_AXI4Lite_AXI4_Bridge; + interface AXI4_Slave_IFC#(`PADDR,`Reg_width,`USERSPACE) axi_slave; + interface AXI4_Lite_Master_IFC#(`PADDR,64,`USERSPACE) axi4_lite_master; + endinterface + + typedef enum {RegularReq,BurstReq} BridgeState deriving (Bits,Eq,FShow); + + (*synthesize*) + module mkAXI4Lite_AXI4_Bridge#(Clock fast_clock, Reset fast_reset)(Ifc_AXI4Lite_AXI4_Bridge); + AXI4_Slave_Xactor_IFC #(`PADDR, `Reg_width, `USERSPACE) s_xactor <- mkAXI4_Slave_Xactor(clocked_by fast_clock, reset_by fast_reset); + AXI4_Lite_Master_Xactor_IFC #(`PADDR,`Reg_width,`USERSPACE) m_xactor <- mkAXI4_Lite_Master_Xactor; + Reg#(BridgeState) rd_state <-mkReg(RegularReq,clocked_by fast_clock, reset_by fast_reset); + Reg#(BridgeState) wr_state <-mkReg(RegularReq,clocked_by fast_clock, reset_by fast_reset); + Reg#(Bit#(4)) rd_id<-mkReg(0); + Reg#(Bit#(4)) wr_id<-mkReg(0); + Reg#(Bit#(8)) request_counter<-mkReg(0,clocked_by fast_clock, reset_by fast_reset); + Reg#(Bit#(8)) rd_response_counter<-mkReg(0); + Reg#(Bit#(8)) wr_response_counter <- mkReg(0); + Reg#(Bit#(8)) sync_rdburst_value <-mkSyncRegToCC(0,fast_clock,fast_reset); + Reg#(Bit#(8)) sync_wrburst_value <-mkSyncRegToCC(0,fast_clock,fast_reset); + Reg#(AXI4_Rd_Addr #(`PADDR,`USERSPACE)) rg_read_packet <-mkReg(?,clocked_by fast_clock , reset_by fast_reset); + Reg#(AXI4_Wr_Addr #(`PADDR,`USERSPACE)) rg_write_packet<-mkReg(?,clocked_by fast_clock , reset_by fast_reset); + + /*=== FIFOs to synchronize data between the two clock domains ====*/ + SyncFIFOIfc#(AXI4_Rd_Addr #(`PADDR,`USERSPACE)) ff_rd_addr <- mkSyncFIFOToCC(1,fast_clock,fast_reset); + SyncFIFOIfc#(AXI4_Wr_Addr #(`PADDR, `USERSPACE)) ff_wr_addr <- mkSyncFIFOToCC(1,fast_clock,fast_reset); + SyncFIFOIfc#(AXI4_Wr_Data #(`Reg_width)) ff_wr_data <- mkSyncFIFOToCC(1,fast_clock,fast_reset); + + SyncFIFOIfc#(AXI4_Rd_Data #(`Reg_width,`USERSPACE)) ff_rd_resp <- mkSyncFIFOFromCC(1,fast_clock); + SyncFIFOIfc#(AXI4_Wr_Resp #(`USERSPACE)) ff_wr_resp <- mkSyncFIFOFromCC(1,fast_clock); + /*=================================================================*/ + + + // These rule will receive the read request from the AXI4 fabric and pass it on to the AXI4Lite fabric. + // If the request is a burst then they are broken down to individual axi4lite read requests. These + // are carried out in the next rule. + rule capture_read_requests_from_Axi4(rd_state==RegularReq); + let request<-pop_o(s_xactor.o_rd_addr); + ff_rd_addr.enq(request); + rg_read_packet<=request; + sync_rdburst_value<=request.arlen; + if(request.arlen!=0) begin + rd_state<=BurstReq; + end + endrule + // In case a read-burst request is received on the fast bus, then the bursts have to broken down into + // individual slow-bus read requests. + // This is rule is fired after the first read-burst request is sent to the slow_bus. This rule will continue to + // fire as long as the slow bus has capacity to receive a new request and the burst is not complete. + // the difference between the each individual requests on the slow bus is only the address. All other + // parameters remain the same. + rule generate_bust_read_requests(rd_state==BurstReq); + let request=rg_read_packet; + request.araddr=burst_address_generator(request.arlen, request.arsize, request.arburst,request.araddr); + rg_read_packet<=request; + ff_rd_addr.enq(request); + if(request.arlen==request_counter)begin + rd_state<=RegularReq; + request_counter<=0; + end + else + request_counter<=request_counter+1; + endrule + rule send_read_request_on_slow_bus; + let request=ff_rd_addr.first; + ff_rd_addr.deq; + let lite_request = AXI4_Lite_Rd_Addr {araddr: request.araddr, arsize:request.arsize,aruser: 0}; // arburst: 00-FIXED 01-INCR 10-WRAP + m_xactor.i_rd_addr.enq(lite_request); + rd_id<=request.arid; + endrule + // This rule will capture the write request from the AXI4 fabric and pass it on to the AXI4Lite fabric. + // In case of burst requests, they are broken down to individual requests of axi4lite writes. Care + // needs to be taken when writes are of different sizes in settin the write-strobe correctly. + rule capture_write_requests_from_Axi4(wr_state==RegularReq); + let wr_addr_req <- pop_o (s_xactor.o_wr_addr); + let wr_data_req <- pop_o (s_xactor.o_wr_data); + ff_wr_addr.enq(wr_addr_req); + ff_wr_data.enq(wr_data_req); + rg_write_packet<=wr_addr_req; + sync_wrburst_value <= wr_addr_req.awlen; + if(wr_addr_req.awlen!=0) begin + wr_state<=BurstReq; + end + `ifdef verbose $display($time,"\tAXIBRIDGE: Write Request"); `endif + `ifdef verbose $display($time,"\tAddress Channel :",fshow(wr_addr_req)); `endif + `ifdef verbose $display($time,"\tData Channel :",fshow(wr_data_req)); `endif + endrule + // In case a write-burst request is received on the fast bus, then the bursts have to broken down into + // individual slow-bus write requests. + // This is rule is fired after the first write-burst request is sent to the slow_bus. This rule will continue to + // fire as long as the slow bus has capacity to receive a new request and the burst is not complete i.e. + // fast bust xactor does not send wlast asserted. + // The difference between the each individual requests on the slow bus is only the address. All other + // parameters remain the same. + rule generate_bust_write_requests(wr_state==BurstReq); + let request=rg_write_packet; + request.awaddr=burst_address_generator(request.awlen, request.awsize, request.awburst,request.awaddr); + let wr_data_req <- pop_o (s_xactor.o_wr_data); + ff_wr_addr.enq(request); + ff_wr_data.enq(wr_data_req); + rg_write_packet<=request; + if(wr_data_req.wlast)begin + wr_state<=RegularReq; + end + `ifdef verbose $display($time,"\tAXIBRIDGE: Burst Write Request"); `endif + `ifdef verbose $display($time,"\tAddress Channel :",fshow(rg_write_packet)); `endif + `ifdef verbose $display($time,"\tData Channel :",fshow(wr_data_req)); `endif + endrule + rule send_write_request_on_slow_bus; + let wr_addr_req = ff_wr_addr.first; + let wr_data_req = ff_wr_data.first; + ff_wr_data.deq; + ff_wr_addr.deq; + let aw = AXI4_Lite_Wr_Addr {awaddr: wr_addr_req.awaddr, awuser:0, awsize: wr_addr_req.awsize}; // arburst: 00-FIXED 01-INCR 10-WRAP + let w = AXI4_Lite_Wr_Data {wdata: wr_data_req.wdata, wstrb: wr_data_req.wstrb}; + m_xactor.i_wr_addr.enq(aw); + m_xactor.i_wr_data.enq(w); + wr_id<=wr_addr_req.awid; + endrule + + // This rule forwards the read response from the AXI4Lite to the AXI4 fabric. + rule capture_read_responses; + let response <- pop_o (m_xactor.o_rd_data); + AXI4_Resp rresp= case(response.rresp) + AXI4_LITE_OKAY : AXI4_OKAY; + AXI4_LITE_EXOKAY: AXI4_EXOKAY; + AXI4_LITE_SLVERR: AXI4_SLVERR; + AXI4_LITE_DECERR: AXI4_DECERR; + default: AXI4_SLVERR; endcase; + AXI4_Rd_Data#(`Reg_width,0) r = AXI4_Rd_Data {rresp: rresp, rdata: response.rdata ,rlast:rd_response_counter==sync_rdburst_value, ruser: 0, rid:rd_id}; + if(rd_response_counter==sync_rdburst_value) + rd_response_counter<=0; + else + rd_response_counter<=rd_response_counter+1; + ff_rd_resp.enq(r); + endrule + rule send_read_response_on_fast_bus; + ff_rd_resp.deq; + s_xactor.i_rd_data.enq(ff_rd_resp.first); + endrule + rule capture_write_responses; + let response<-pop_o(m_xactor.o_wr_resp); + AXI4_Resp bresp= case(response.bresp) + AXI4_LITE_OKAY : AXI4_OKAY; + AXI4_LITE_EXOKAY: AXI4_EXOKAY; + AXI4_LITE_SLVERR: AXI4_SLVERR; + AXI4_LITE_DECERR: AXI4_DECERR; + default: AXI4_SLVERR; endcase; + let b = AXI4_Wr_Resp {bresp: bresp, buser:0, bid:wr_id}; + if(wr_response_counter == sync_wrburst_value) begin + ff_wr_resp.enq(b); + wr_response_counter <= 0; + end + else + wr_response_counter <= wr_response_counter + 1; + endrule + rule send_write_response_on_fast_bus; + ff_wr_resp.deq; + s_xactor.i_wr_resp.enq(ff_wr_resp.first); + endrule + interface axi_slave=s_xactor.axi_side; + interface axi4_lite_master=m_xactor.axi_side; + endmodule +endpackage