1919
2020#include " gmock/gmock.h"
2121#include " gtest/gtest.h"
22+ #include " absl/status/status.h"
2223#include " absl/status/statusor.h"
2324#include " xls/common/status/matchers.h"
2425#include " xls/interpreter/channel_queue.h"
@@ -36,8 +37,10 @@ namespace xls {
3637namespace {
3738
3839using status_testing::IsOkAndHolds;
39- using testing::ElementsAre;
40- using testing::Optional;
40+ using status_testing::StatusIs;
41+ using ::testing::ElementsAre;
42+ using ::testing::HasSubstr;
43+ using ::testing::Optional;
4144
4245TEST_P (ProcEvaluatorTestBase, EmptyProc) {
4346 auto package = CreatePackage ();
@@ -543,6 +546,247 @@ TEST_P(ProcEvaluatorTestBase, ConditionalSendProc) {
543546 EXPECT_THAT (queue.Read (), Optional (Value (UBits (4 , 32 ))));
544547}
545548
549+ TEST_P (ProcEvaluatorTestBase, UnconditionalNextProc) {
550+ if (!GetParam ().SupportsNextValue ()) {
551+ GTEST_SKIP () << " Evaluator does not support next_value" ;
552+ }
553+
554+ // Create an output-only proc which increments its counter value each
555+ // iteration, using explicit next_value nodes.
556+ Package package (TestName ());
557+ XLS_ASSERT_OK_AND_ASSIGN (
558+ Channel * channel,
559+ package.CreateStreamingChannel (" counter_out" , ChannelOps::kSendOnly ,
560+ package.GetBitsType (32 )));
561+
562+ ProcBuilder pb (" counter" , /* token_name=*/ " tok" , &package);
563+ BValue counter = pb.StateElement (" counter" , Value (UBits (0 , 32 )));
564+ BValue send = pb.Send (channel, pb.GetTokenParam (), counter);
565+ BValue incremented_counter = pb.Add (counter, pb.Literal (UBits (1 , 32 )));
566+ pb.Next (/* param=*/ counter, /* value=*/ incremented_counter);
567+ XLS_ASSERT_OK_AND_ASSIGN (Proc * proc, pb.Build (send));
568+
569+ std::unique_ptr<ChannelQueueManager> queue_manager =
570+ GetParam ().CreateQueueManager (&package);
571+ std::unique_ptr<ProcEvaluator> evaluator = GetParam ().CreateEvaluator (
572+ FindProc (" counter" , &package), queue_manager.get ());
573+
574+ ChannelQueue& queue = queue_manager->GetQueue (channel);
575+ XLS_ASSERT_OK_AND_ASSIGN (
576+ ChannelInstance * channel_instance,
577+ queue_manager->elaboration ().GetUniqueInstance (channel));
578+
579+ std::unique_ptr<ProcContinuation> continuation = evaluator->NewContinuation (
580+ queue_manager->elaboration ().GetUniqueInstance (proc).value ());
581+
582+ EXPECT_THAT (evaluator->Tick (*continuation),
583+ IsOkAndHolds (TickResult{
584+ .execution_state = TickExecutionState::kSentOnChannel ,
585+ .channel_instance = channel_instance,
586+ .progress_made = true }));
587+ EXPECT_THAT (
588+ evaluator->Tick (*continuation),
589+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
590+ .channel_instance = std::nullopt ,
591+ .progress_made = true }));
592+ EXPECT_EQ (queue.GetSize (), 1 );
593+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (0 , 32 ))));
594+
595+ EXPECT_THAT (evaluator->Tick (*continuation),
596+ IsOkAndHolds (TickResult{
597+ .execution_state = TickExecutionState::kSentOnChannel ,
598+ .channel_instance = channel_instance,
599+ .progress_made = true }));
600+ EXPECT_THAT (
601+ evaluator->Tick (*continuation),
602+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
603+ .channel_instance = std::nullopt ,
604+ .progress_made = true }));
605+ EXPECT_EQ (queue.GetSize (), 1 );
606+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (1 , 32 ))));
607+
608+ EXPECT_THAT (evaluator->Tick (*continuation),
609+ IsOkAndHolds (TickResult{
610+ .execution_state = TickExecutionState::kSentOnChannel ,
611+ .channel_instance = channel_instance,
612+ .progress_made = true }));
613+ EXPECT_THAT (
614+ evaluator->Tick (*continuation),
615+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
616+ .channel_instance = std::nullopt ,
617+ .progress_made = true }));
618+ EXPECT_EQ (queue.GetSize (), 1 );
619+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (2 , 32 ))));
620+ }
621+
622+ TEST_P (ProcEvaluatorTestBase, ConditionalNextProc) {
623+ if (!GetParam ().SupportsNextValue ()) {
624+ GTEST_SKIP () << " Evaluator does not support next_value" ;
625+ }
626+
627+ // Create an output-only proc which increments its counter value only every
628+ // other iteration.
629+ Package package (TestName ());
630+ XLS_ASSERT_OK_AND_ASSIGN (
631+ Channel * channel,
632+ package.CreateStreamingChannel (" slow_counter_out" , ChannelOps::kSendOnly ,
633+ package.GetBitsType (32 )));
634+
635+ ProcBuilder pb (" slow_counter" , /* token_name=*/ " tok" , &package);
636+ BValue counter = pb.StateElement (" counter" , Value (UBits (0 , 32 )));
637+ BValue iteration = pb.StateElement (" iteration" , Value (UBits (0 , 32 )));
638+ BValue send = pb.Send (channel, pb.GetTokenParam (), counter);
639+ BValue incremented_counter = pb.Add (counter, pb.Literal (UBits (1 , 32 )));
640+ BValue odd_iteration = pb.Eq (pb.BitSlice (iteration, /* start=*/ 0 , /* width=*/ 1 ),
641+ pb.Literal (UBits (1 , 1 )));
642+ pb.Next (/* param=*/ counter, /* value=*/ incremented_counter,
643+ /* pred=*/ odd_iteration);
644+ pb.Next (/* param=*/ iteration,
645+ /* value=*/ pb.Add (iteration, pb.Literal (UBits (1 , 32 ))));
646+ XLS_ASSERT_OK_AND_ASSIGN (Proc * proc, pb.Build (send));
647+
648+ std::unique_ptr<ChannelQueueManager> queue_manager =
649+ GetParam ().CreateQueueManager (&package);
650+ std::unique_ptr<ProcEvaluator> evaluator = GetParam ().CreateEvaluator (
651+ FindProc (" slow_counter" , &package), queue_manager.get ());
652+
653+ ChannelQueue& queue = queue_manager->GetQueue (channel);
654+ XLS_ASSERT_OK_AND_ASSIGN (
655+ ChannelInstance * channel_instance,
656+ queue_manager->elaboration ().GetUniqueInstance (channel));
657+
658+ std::unique_ptr<ProcContinuation> continuation = evaluator->NewContinuation (
659+ queue_manager->elaboration ().GetUniqueInstance (proc).value ());
660+ EXPECT_THAT (evaluator->Tick (*continuation),
661+ IsOkAndHolds (TickResult{
662+ .execution_state = TickExecutionState::kSentOnChannel ,
663+ .channel_instance = channel_instance,
664+ .progress_made = true }));
665+ EXPECT_THAT (
666+ evaluator->Tick (*continuation),
667+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
668+ .channel_instance = std::nullopt ,
669+ .progress_made = true }));
670+ EXPECT_EQ (queue.GetSize (), 1 );
671+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (0 , 32 ))));
672+
673+ EXPECT_THAT (evaluator->Tick (*continuation),
674+ IsOkAndHolds (TickResult{
675+ .execution_state = TickExecutionState::kSentOnChannel ,
676+ .channel_instance = channel_instance,
677+ .progress_made = true }));
678+ EXPECT_THAT (
679+ evaluator->Tick (*continuation),
680+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
681+ .channel_instance = std::nullopt ,
682+ .progress_made = true }));
683+ EXPECT_EQ (queue.GetSize (), 1 );
684+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (0 , 32 ))));
685+
686+ EXPECT_THAT (evaluator->Tick (*continuation),
687+ IsOkAndHolds (TickResult{
688+ .execution_state = TickExecutionState::kSentOnChannel ,
689+ .channel_instance = channel_instance,
690+ .progress_made = true }));
691+ EXPECT_THAT (
692+ evaluator->Tick (*continuation),
693+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
694+ .channel_instance = std::nullopt ,
695+ .progress_made = true }));
696+ EXPECT_EQ (queue.GetSize (), 1 );
697+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (1 , 32 ))));
698+
699+ EXPECT_THAT (evaluator->Tick (*continuation),
700+ IsOkAndHolds (TickResult{
701+ .execution_state = TickExecutionState::kSentOnChannel ,
702+ .channel_instance = channel_instance,
703+ .progress_made = true }));
704+ EXPECT_THAT (
705+ evaluator->Tick (*continuation),
706+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
707+ .channel_instance = std::nullopt ,
708+ .progress_made = true }));
709+ EXPECT_EQ (queue.GetSize (), 1 );
710+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (1 , 32 ))));
711+
712+ EXPECT_THAT (evaluator->Tick (*continuation),
713+ IsOkAndHolds (TickResult{
714+ .execution_state = TickExecutionState::kSentOnChannel ,
715+ .channel_instance = channel_instance,
716+ .progress_made = true }));
717+ EXPECT_THAT (
718+ evaluator->Tick (*continuation),
719+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
720+ .channel_instance = std::nullopt ,
721+ .progress_made = true }));
722+ EXPECT_EQ (queue.GetSize (), 1 );
723+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (2 , 32 ))));
724+ }
725+
726+ TEST_P (ProcEvaluatorTestBase, CollidingNextValuesProc) {
727+ if (!GetParam ().SupportsNextValue ()) {
728+ GTEST_SKIP () << " Evaluator does not support next_value" ;
729+ }
730+
731+ // Create an output-only proc which increments its counter value only every
732+ // other iteration - but also tries to set the counter value to a different
733+ // value.
734+ Package package (TestName ());
735+ XLS_ASSERT_OK_AND_ASSIGN (
736+ Channel * channel,
737+ package.CreateStreamingChannel (" slow_counter_out" , ChannelOps::kSendOnly ,
738+ package.GetBitsType (32 )));
739+
740+ ProcBuilder pb (" slow_counter" , /* token_name=*/ " tok" , &package);
741+ BValue counter = pb.StateElement (" counter" , Value (UBits (0 , 32 )));
742+ BValue iteration = pb.StateElement (" iteration" , Value (UBits (0 , 32 )));
743+ BValue send = pb.Send (channel, pb.GetTokenParam (), counter);
744+ BValue incremented_counter = pb.Add (counter, pb.Literal (UBits (1 , 32 )));
745+ BValue odd_iteration = pb.Eq (pb.BitSlice (iteration, /* start=*/ 0 , /* width=*/ 1 ),
746+ pb.Literal (UBits (1 , 1 )));
747+ pb.Next (/* param=*/ counter, /* value=*/ incremented_counter,
748+ /* pred=*/ odd_iteration);
749+ pb.Next (/* param=*/ counter, /* value=*/ pb.Literal (UBits (0 , 32 )));
750+ pb.Next (/* param=*/ iteration,
751+ /* value=*/ pb.Add (iteration, pb.Literal (UBits (1 , 32 ))));
752+ XLS_ASSERT_OK_AND_ASSIGN (Proc * proc, pb.Build (send));
753+
754+ std::unique_ptr<ChannelQueueManager> queue_manager =
755+ GetParam ().CreateQueueManager (&package);
756+ std::unique_ptr<ProcEvaluator> evaluator = GetParam ().CreateEvaluator (
757+ FindProc (" slow_counter" , &package), queue_manager.get ());
758+
759+ ChannelQueue& queue = queue_manager->GetQueue (channel);
760+ XLS_ASSERT_OK_AND_ASSIGN (
761+ ChannelInstance * channel_instance,
762+ queue_manager->elaboration ().GetUniqueInstance (channel));
763+
764+ std::unique_ptr<ProcContinuation> continuation = evaluator->NewContinuation (
765+ queue_manager->elaboration ().GetUniqueInstance (proc).value ());
766+ EXPECT_THAT (evaluator->Tick (*continuation),
767+ IsOkAndHolds (TickResult{
768+ .execution_state = TickExecutionState::kSentOnChannel ,
769+ .channel_instance = channel_instance,
770+ .progress_made = true }));
771+ EXPECT_THAT (
772+ evaluator->Tick (*continuation),
773+ IsOkAndHolds (TickResult{.execution_state = TickExecutionState::kCompleted ,
774+ .channel_instance = std::nullopt ,
775+ .progress_made = true }));
776+ EXPECT_EQ (queue.GetSize (), 1 );
777+ EXPECT_THAT (queue.Read (), Optional (Value (UBits (0 , 32 ))));
778+
779+ EXPECT_THAT (evaluator->Tick (*continuation),
780+ IsOkAndHolds (TickResult{
781+ .execution_state = TickExecutionState::kSentOnChannel ,
782+ .channel_instance = channel_instance,
783+ .progress_made = true }));
784+ EXPECT_THAT (evaluator->Tick (*continuation),
785+ StatusIs (absl::StatusCode::kAlreadyExists ,
786+ HasSubstr (" Multiple active next values for param "
787+ " \" counter\" in a single activation" )));
788+ }
789+
546790TEST_P (ProcEvaluatorTestBase, OneToTwoDemux) {
547791 // Build a proc which acts as a one-to-two demux. Data channels are streaming,
548792 // and the selector is a single-value channel.
0 commit comments