• How to define multi-cycle timing constraints in Lattice iCEcube2 (synp

    From Stef@3:633/280.2 to All on Wed Jul 12 01:51:00 2023
    Subject: How to define multi-cycle timing constraints in Lattice iCEcube2
    (synplify)

    In a design, I have a number of counters that have an enable that is
    only active every N periods of the clock signal. With only a clock of
    200MHz in the timing constraints, the counters are assumed to run at
    200MHz and timing fails. As these counters actually run at much lower
    speed, I know they will work. But how to convince the tools (Lattice
    iCEcube2) that all is OK?

    I am trying to add multi-cycle constraints, but cannot get them to work.
    The standard iCEcube timing constraints editor can only create
    multi-cycle from input pin to output pin, and that does not even work. I
    need to add a multi-cycle constraint on an internal counter. Preferrably
    with some wildcard, as I have an array of counters, each with a number
    of bits. Specifying each separtely would require a lot of lines.

    One example of a failed path (from the iCEcube timing analyzer)
    Start protection_inst.filter_array_3_filter_inst.filter_count_1_LC_5_5_1/lcout
    End protection_inst.filter_array_3_filter_inst.filter_count_5_LC_5_5_5/in3
    Reference gen_pll_wc_lattice_pll_inst.wc_lattice_pll_inst_pll/PLLOUTCORE
    Setup Constraint 5262(p)
    Path Slack -762(p)

    Simply adding this does not work:
    set_multicycle_path -to [get_cells {protection_inst.filter_array_3_filter_inst.filter_count_5_LC_5_5_5/in3}] 8

    Synplify output: protection_inst.filter_array_3_filter_inst.filter_count_5_LC_5_5_5/in3])
    (multi path 8) was not applied to the design because none of the '-to'
    objects specified by the constraint exist in the design

    I've tried get_nets and get_pins instead of get_cells, same result. Also
    tried other parts of the instance names with wildcards, nothing found.

    How to specify multi-cycle paths for the feedback of these internal
    counters?


    --
    Stef

    He who has the courage to laugh is almost as much a master of the world
    as he who is ready to die.
    -- Giacomo Leopardi

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: Newsxs (3:633/280.2@fidonet)
  • From Stef@3:633/280.2 to All on Thu Jul 13 21:18:30 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice
    iCEcube2 (synplify)

    On 2023-07-11 Stef wrote in comp.arch.fpga:

    After some rewriting of the counter code, they now meet timing when
    running at 200MHz. So all timing even single cycle okay, no immediate
    need for multi-cycle constraints.

    But the question remains valid: how to define multi-cycle constraints?
    It is a bit absurd to require passing timing for a 200MHz clock when
    some of these counters only run at a few kHz.

    --
    Stef

    50% of the manual is in .pdf readme files

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: Newsxs (3:633/280.2@fidonet)
  • From KJ@3:633/280.2 to All on Fri Jul 14 20:30:45 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice iCEcube2 (synplify)

    On Thursday, July 13, 2023 at 7:18:37=E2=80=AFAM UTC-4, Stef wrote:
    On 2023-07-11 Stef wrote in comp.arch.fpga:=20
    =20
    But the question remains valid: how to define multi-cycle constraints?=20
    It is a bit absurd to require passing timing for a 200MHz clock when=20
    some of these counters only run at a few kHz.=20
    =20
    I haven't used Lattice tools, so I don't know the answer to your question. =
    However, your complaint "...a bit absurd to require passing timing for a 2= 00MHz clock when some of these counters only run at a few kHz" is very wron=
    g. Every bit in a counter will depend on all of the lower/less significant=
    bits. In other words, bits 7-1 will depend on bit 0. Bit 0 will be toggl= ing at a 200 MHz rate so the logic for computing bit 7 will have to operate=
    at that speed as well. 5 ns after the counter is at 11111111 all bits wil=
    l be flipping to 00000000. To see these logic paths, just take a look at t=
    he post-fit logic and you'll see that what I described is correct.

    So the entire premise of your question is not correct. Even if you do find=
    out how to add multi-cycle constraints in your tool, you'll want to be car= eful. The tools don't know that the constraint you entered is not correct = which means it won't report a valid timing problem because of this error. = You could end up scratching your head trying to figure out why things are a= cting flaky (i.e. an actual timing problem) when the tool says that the tim= ing is correct. As far as I know, there is no 'error checking' to see that=
    user supplied constraints are in fact correct.

    Kevin Jennings

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: ---:- FTN<->UseNet Gate -:--- (3:633/280.2@fidonet)
  • From Richard Damon@3:633/280.2 to All on Fri Jul 14 21:23:10 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice iCEcube2
    (synplify)

    On 7/14/23 6:30 AM, KJ wrote:
    On Thursday, July 13, 2023 at 7:18:37 AM UTC-4, Stef wrote:
    On 2023-07-11 Stef wrote in comp.arch.fpga:

    But the question remains valid: how to define multi-cycle constraints?
    It is a bit absurd to require passing timing for a 200MHz clock when
    some of these counters only run at a few kHz.

    I haven't used Lattice tools, so I don't know the answer to your question. However, your complaint "...a bit absurd to require passing timing for a 200MHz clock when some of these counters only run at a few kHz" is very wrong. Every bit in a counter will depend on all of the lower/less significant bits. In other words, bits 7-1 will depend on bit 0. Bit 0 will be toggling at a 200 MHz rate so the logic for computing bit 7 will have to operate at that speed as well. 5 ns after the counter is at 11111111 all bits will be flipping to 00000000. To see these logic paths, just take a look at the post-fit logic and you'll see that what I described is correct.

    So the entire premise of your question is not correct. Even if you do find out how to add multi-cycle constraints in your tool, you'll want to be careful. The tools don't know that the constraint you entered is not correct which means it won't report a valid timing problem because of this error. You could end up scratching your head trying to figure out why things are acting flaky (i.e. an actual timing problem) when the tool says that the timing is correct. As far as I know, there is no 'error checking' to see that user supplied constraints are in fact correct.

    Kevin Jennings

    The OP said that it was a counter with enable, and the enable only
    occured every N cycles, so your statement isn't true.

    The enable -> counter-ff needs to meet the requirement, but that signal
    will tend to feed the last part of the logic, so shouldn't be a problem,
    the timing limitation being the carry chain from a low bit to a high bit.

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: Forte - www.forteinc.com (3:633/280.2@fidonet)
  • From KJ@3:633/280.2 to All on Sat Jul 15 00:11:58 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice iCEcube2 (synplify)

    On Friday, July 14, 2023 at 7:23:17=E2=80=AFAM UTC-4, Richard Damon wrote:
    On 7/14/23 6:30 AM, KJ wrote:=20

    The OP said that it was a counter with enable, and the enable only=20
    occured every N cycles, so your statement isn't true.=20
    =20
    The enable -> counter-ff needs to meet the requirement, but that signal=
    =20
    will tend to feed the last part of the logic, so shouldn't be a problem,=
    =20
    the timing limitation being the carry chain from a low bit to a high bit.

    What I said in the earlier post is accurate, but you are making assumptions=
    about the enable signal logic path that might not be accurate. The enable=
    signal could be just another logic signal that feeds into logic that gener= ates the D input of the flip flops of the counter. Or, IF the part has a D= -FF primitive with a dedicated Clock Enable input, AND that clock enable in= put is actually used by the tool then the enable-->counter FF path would be=
    separate from the D input logic. There might be tool specific, non-portab=
    le ways to implement this but you're still going to have to depend on the t= ool to do what you think is right.

    If the enable signal is just another input to the logic that feeds into the=
    D input of the flip flops of the counter then there are no guarantees that=
    the signal will 'tend to feed the last part of the logic' as you mentioned=
    (although it might).

    Since the OP said he rewrote the counter logic and timing now works, presum= ably he simplified the logic that went into determining when the counter sh= ould increment. A simple way to accomplish this is to make sure there is a=
    n actual discrete enable signal by syncing to the clock before using that s= ignal to enable the counter.

    In any case, this doesn't address the OP's question about how to specify m= ulti-cycle constraints in the Lattice tools so this is all a tangent but go=
    od discussion.

    Kevin Jennings

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: ---:- FTN<->UseNet Gate -:--- (3:633/280.2@fidonet)
  • From Stef@3:633/280.2 to All on Sat Jul 15 02:50:49 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice
    iCEcube2 (synplify)

    On 2023-07-14 KJ wrote in comp.arch.fpga:
    On Friday, July 14, 2023 at 7:23:17 AM UTC-4, Richard Damon wrote:
    On 7/14/23 6:30 AM, KJ wrote:

    The OP said that it was a counter with enable, and the enable only
    occured every N cycles, so your statement isn't true.

    The enable -> counter-ff needs to meet the requirement, but that signal
    will tend to feed the last part of the logic, so shouldn't be a problem,
    the timing limitation being the carry chain from a low bit to a high bit.

    What I said in the earlier post is accurate, but you are making assumptions about the enable signal logic path that might not be accurate. The enable signal could be just another logic signal that feeds into logic that generates the D input of the flip flops of the counter. Or, IF the part has a D-FF primitive with a dedicated Clock Enable input, AND that clock enable input is actually used by the tool then the enable-->counter FF path would be separate from the D input logic. There might be tool specific, non-portable ways to implement this but you're still going to have to depend on the tool to do what you think is right.

    If the enable signal is just another input to the logic that feeds into the D input of the flip flops of the counter then there are no guarantees that the signal will 'tend to feed the last part of the logic' as you mentioned (although it might).

    Since the OP said he rewrote the counter logic and timing now works, presumably he simplified the logic that went into determining when the counter should increment. A simple way to accomplish this is to make sure there is an actual discrete enable signal by syncing to the clock before using that signal to enable the counter.


    Okay, you're both right. :-)

    In the counter design I had an enable signal for the clock and since the Lattice hardware has DFFs with an enable input, I _assumed_ this would
    be used. And as always: Assumption is the mother of all f*ckups.

    When examining the P&R result, I found the enable was included in the
    feedback logic and not as a direct enable signal to the DFF. :-(

    The counters need to count up and down and stop at the end points (0 and
    max). And there is an output signal that switches only at the end
    points. This was the original counter code:

    process(clk, reset)
    begin
    if reset = '1' then
    filter_count <= 0;
    sig_out <= '0';
    elsif rising_edge(clk) and clk_enable = '1' then
    if sig_in = '1' then
    if filter_count < count_max then
    filter_count <= filter_count + 1;
    else
    sig_out <= '1';
    end if;
    else
    if filter_count > 0 then
    filter_count <= filter_count - 1;
    else
    sig_out <= '0';
    end if;
    end if;
    end if;
    end process;

    This resulted in logic with a DFF with no enable signal and the enable
    included in the counter feedback logic.

    Changing the counter part to this:

    if sig_in = '1' then
    if filter_count = count_max then
    sig_out <= '1';
    else
    filter_count <= filter_count + 1;
    end if;
    else
    if filter_count = 0 then
    sig_out <= '0';
    else
    filter_count <= filter_count - 1;
    end if;
    end if;

    Resulted in using DFFs with an enable signal, which connects to the
    clk_enable in the code. And as the enable is removed from the feedback
    logic, this logic is simpler and can now meet the single cycle 200MHz
    timing as an added bonus.

    This shows you indeed have to be very carefull when wanting to use
    multi-cycle constraints. Make sure the logic is indeed implemented with
    an actual clock enable on the DFF. But if this is the case and timing of
    the enable is okay (don't set multi-cycle on that one), this counter
    could probably handle an even faster clock. If only the tools understood
    the multi-cycle nature of the counter.

    And that brings us to this point:

    In any case, this doesn't address the OP's question about how to specify multi-cycle constraints in the Lattice tools so this is all a tangent but good discussion.


    The lattice tools use synplify, which is used by multiple vendors. So I
    would expect this syntax not to be vendor specific. Although the signal
    name mangling after synthesis may vary between vendors?

    Al my searches so far turned up only very simple examples like:
    set_multicycle_path -end -setup -to [get_cells regb] 2

    Which I haven't been able to translate to something that will work in my design.

    And when I look at the code again, this is just as well. :-)

    The sig_in signal can change on every 200MHz clock cycle, so logic
    handling that signal should pass 200MHz timing. And this is in the
    counter feedback logic. For the counter to be truly multicycle, sig_in
    should be passed through a DFF with the same clock enable as well.
    So be very carefull if I ever figure out how to do multi-cycle
    constaints. Just keeping everything on max speed timing is at least a
    safe aproach.

    --
    Stef

    According to all the latest reports, there was no truth in any of the
    earlier reports.

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: Newsxs (3:633/280.2@fidonet)
  • From Richard Damon@3:633/280.2 to All on Sat Jul 15 09:56:30 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice iCEcube2
    (synplify)

    On 7/14/23 10:11 AM, KJ wrote:
    On Friday, July 14, 2023 at 7:23:17 AM UTC-4, Richard Damon wrote:
    On 7/14/23 6:30 AM, KJ wrote:

    The OP said that it was a counter with enable, and the enable only
    occured every N cycles, so your statement isn't true.

    The enable -> counter-ff needs to meet the requirement, but that signal
    will tend to feed the last part of the logic, so shouldn't be a problem,
    the timing limitation being the carry chain from a low bit to a high bit.

    What I said in the earlier post is accurate, but you are making assumptions about the enable signal logic path that might not be accurate. The enable signal could be just another logic signal that feeds into logic that generates the D input of the flip flops of the counter. Or, IF the part has a D-FF primitive with a dedicated Clock Enable input, AND that clock enable input is actually used by the tool then the enable-->counter FF path would be separate from the D input logic. There might be tool specific, non-portable ways to implement this but you're still going to have to depend on the tool to do what you think is right.

    If the enable signal is just another input to the logic that feeds into the D input of the flip flops of the counter then there are no guarantees that the signal will 'tend to feed the last part of the logic' as you mentioned (although it might).

    Since the OP said he rewrote the counter logic and timing now works, presumably he simplified the logic that went into determining when the counter should increment. A simple way to accomplish this is to make sure there is an actual discrete enable signal by syncing to the clock before using that signal to enable the counter.

    In any case, this doesn't address the OP's question about how to specify multi-cycle constraints in the Lattice tools so this is all a tangent but good discussion.

    Kevin Jennings

    Yes, it is possible to generate code that the enable signal doesn't end
    up on the fast path, but any compiler that does that without being
    forced into is very bad, as the natural equations would become a mux on
    the enable selecting between the current results and the incremented value.

    The one way that I can think of to put the enable on the slow path is to
    make the equations D <= Q + enable, and if that is how you wrote it, you deserve the slowness.

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: Forte - www.forteinc.com (3:633/280.2@fidonet)
  • From KJ@3:633/280.2 to All on Sat Jul 15 23:45:12 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice iCEcube2 (synplify)

    On Friday, July 14, 2023 at 7:56:37=E2=80=AFPM UTC-4, Richard Damon wrote:

    Yes, it is possible to generate code that the enable signal doesn't end=
    =20
    up on the fast path, but any compiler that does that without being=20
    forced into is very bad, as the natural equations would become a mux on=
    =20
    the enable selecting between the current results and the incremented valu=
    e.=20
    =20
    The one way that I can think of to put the enable on the slow path is to=
    =20
    make the equations D <=3D Q + enable, and if that is how you wrote it, yo=
    u=20
    deserve the slowness.

    Here's another way putting the enable on the slow path can happen...read th=
    e OP's last post. What he did to get the tool to use the clock enable is t=
    o change logic that was already inside the outer if statement looking at th=
    e clock enable. He changed "if filter_count < count_max" to "if filter_cou=
    nt =3D count_max" and "if filter_count > 0 then" to "if filter_count =3D 0 = then". Using equals instead of less than and greater than typically reduce=
    s the amount of logic that goes into the D input but will have no logical i= mpact on whether a clock enable input to a FF could be used. The outermost=
    "elsif rising_edge(clk) and clk_enable =3D '1' then" is unchanged between = his two approaches.

    By your measure, it seems Synplify used by Lattice is "very bad".

    @Stef
    Here are a couple of multicycle path statements from a working Quartus FPGA=
    design. The only notable difference I see from what you posted is the use=
    of get_keepers instead of get_cells as you had. If I recall correctly, Qu= artus uses Synopsys (or at least the same syntax).

    set_multicycle_path -setup -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]=
    }] 2
    set_multicycle_path -hold -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}=
    ] 2

    On another design, that uses an FPGA from Efinix, their tool Efinity is way=
    more limited. In that design you can only specify multi-cycle paths betwe=
    en clock domains but it doesn't look like you use get_* at all. set_multicycle_path -from Afe_AdcClk_ext -to Afe_ClockX4 -setup -end 2

    Maybe give get_keepers a shot just to see if that is the syntax you need to=
    use to get rid of the error "...was not applied to the design because none=
    of the '-to' objects specified by the constraint exist in the design". Wh= ether or not using multi cycle is useful in this situation (which as you fo= und it was not), it is still useful to be able to use multicycle when it is=
    appropriate (like a single signal crossing clock domains).

    Kevin Jennings

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: ---:- FTN<->UseNet Gate -:--- (3:633/280.2@fidonet)
  • From Richard Damon@3:633/280.2 to All on Sun Jul 16 01:54:24 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice iCEcube2
    (synplify)

    On 7/15/23 9:45 AM, KJ wrote:
    On Friday, July 14, 2023 at 7:56:37 PM UTC-4, Richard Damon wrote:

    Yes, it is possible to generate code that the enable signal doesn't end
    up on the fast path, but any compiler that does that without being
    forced into is very bad, as the natural equations would become a mux on
    the enable selecting between the current results and the incremented value. >>
    The one way that I can think of to put the enable on the slow path is to
    make the equations D <= Q + enable, and if that is how you wrote it, you
    deserve the slowness.

    Here's another way putting the enable on the slow path can happen...read the OP's last post. What he did to get the tool to use the clock enable is to change logic that was already inside the outer if statement looking at the clock enable. He changed "if filter_count < count_max" to "if filter_count = count_max" and "if filter_count > 0 then" to "if filter_count = 0 then". Using equals instead of less than and greater than typically reduces the amount of logic that goes into the D input but will have no logical impact on whether a clock enable input to a FF could be used. The outermost "elsif rising_edge(clk) and clk_enable = '1' then" is unchanged between his two approaches.

    By your measure, it seems Synplify used by Lattice is "very bad".


    No, his clk_enable will end up at the last block of logic for the
    flip-flop, (either using an actual enable pin, or the last LUT feeding
    the flip-flop. My comment was strictly about the timing of the ENABLE
    signal.

    The change improved the logic for the COUNTER part of the logic (since
    equal to a constant is much easier to compute than a less than comparison).

    Its possible that this simplification allowed the compiler to recognize
    that the enable signal was a simple enable and could use that operation
    of the Logic Element, and thus be faster (and allowing more counter
    logic in that last LUT of the Logic Element).

    @Stef
    Here are a couple of multicycle path statements from a working Quartus FPGA design. The only notable difference I see from what you posted is the use of get_keepers instead of get_cells as you had. If I recall correctly, Quartus uses Synopsys (or at least the same syntax).

    set_multicycle_path -setup -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2
    set_multicycle_path -hold -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2

    On another design, that uses an FPGA from Efinix, their tool Efinity is way more limited. In that design you can only specify multi-cycle paths between clock domains but it doesn't look like you use get_* at all.
    set_multicycle_path -from Afe_AdcClk_ext -to Afe_ClockX4 -setup -end 2

    Maybe give get_keepers a shot just to see if that is the syntax you need to use to get rid of the error "...was not applied to the design because none of the '-to' objects specified by the constraint exist in the design". Whether or not using multi cycle is useful in this situation (which as you found it was not), it is still useful to be able to use multicycle when it is appropriate (like a single signal crossing clock domains).

    Kevin Jennings



    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: Forte - www.forteinc.com (3:633/280.2@fidonet)
  • From Stef@3:633/280.2 to All on Mon Jul 17 22:57:13 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice
    iCEcube2 (synplify)

    On 2023-07-15 KJ wrote in comp.arch.fpga:
    ....
    @Stef
    Here are a couple of multicycle path statements from a working Quartus FPGA design. The only notable difference I see from what you posted is the use of get_keepers instead of get_cells as you had. If I recall correctly, Quartus uses Synopsys (or at least the same syntax).

    set_multicycle_path -setup -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2
    set_multicycle_path -hold -end -from [get_keepers {*Avalon_Reset_n_Sreg[*]}] 2

    ....

    Thanks for those suggestions. I'll give them a try when I get back in a
    few weeks.

    --
    Stef

    "Success covers a multitude of blunders."
    -- George Bernard Shaw

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: Newsxs (3:633/280.2@fidonet)
  • From gnuarm.del...@gmail.com@3:633/280.2 to All on Wed Jul 19 17:59:14 2023
    Subject: Re: How to define multi-cycle timing constraints in Lattice iCEcube2 (synplify)

    On Friday, July 14, 2023 at 7:23:17=E2=80=AFAM UTC-4, Richard Damon wrote:
    On 7/14/23 6:30 AM, KJ wrote:=20
    On Thursday, July 13, 2023 at 7:18:37=E2=80=AFAM UTC-4, Stef wrote:=20
    On 2023-07-11 Stef wrote in comp.arch.fpga:=20
    =20
    But the question remains valid: how to define multi-cycle constraints?=
    =20
    It is a bit absurd to require passing timing for a 200MHz clock when=
    =20
    some of these counters only run at a few kHz.=20
    =20
    I haven't used Lattice tools, so I don't know the answer to your questi=
    on. However, your complaint "...a bit absurd to require passing timing for =
    a 200MHz clock when some of these counters only run at a few kHz" is very w= rong. Every bit in a counter will depend on all of the lower/less significa=
    nt bits. In other words, bits 7-1 will depend on bit 0. Bit 0 will be toggl= ing at a 200 MHz rate so the logic for computing bit 7 will have to operate=
    at that speed as well. 5 ns after the counter is at 11111111 all bits will=
    be flipping to 00000000. To see these logic paths, just take a look at the=
    post-fit logic and you'll see that what I described is correct.=20
    =20
    So the entire premise of your question is not correct. Even if you do f=
    ind out how to add multi-cycle constraints in your tool, you'll want to be = careful. The tools don't know that the constraint you entered is not correc=
    t which means it won't report a valid timing problem because of this error.=
    You could end up scratching your head trying to figure out why things are = acting flaky (i.e. an actual timing problem) when the tool says that the ti= ming is correct. As far as I know, there is no 'error checking' to see that=
    user supplied constraints are in fact correct.=20
    =20
    Kevin Jennings
    The OP said that it was a counter with enable, and the enable only=20
    occured every N cycles, so your statement isn't true.=20
    =20
    The enable -> counter-ff needs to meet the requirement, but that signal=
    =20
    will tend to feed the last part of the logic, so shouldn't be a problem,=
    =20
    the timing limitation being the carry chain from a low bit to a high bit.

    If the counter is running with an enable only 1 in N clock cycles, the carr=
    y chain has N clock cycles to propagate. The only signal that needs to be = timed at 1 clock cycle is the enable.=20

    --=20

    Rick C.

    - Get 1,000 miles of free Supercharging
    - Tesla referral code - https://ts.la/richard11209

    --- MBSE BBS v1.0.8 (Linux-x86_64)
    * Origin: ---:- FTN<->UseNet Gate -:--- (3:633/280.2@fidonet)